29 Commits
1.2.0 ... 1.3.1

Author SHA1 Message Date
h2zero
7b40829e77 Release 1.3.1 2021-08-04 18:55:34 -06:00
h2zero
b24597ac56 Remove definition of btInUse().
This reverts a previous change @2e1d78ff that caused compiling/linking errors due to the redefinition of btInUse() when another linked library uses the Arduino bluetooth code.
2021-08-03 12:43:34 -06:00
h2zero
9961c5605c Update docs version. 2021-08-02 21:25:17 -06:00
h2zero
d4e4074f5a Release 1.3.0 2021-08-02 21:14:12 -06:00
h2zero
e5edc9d59e Add missing return documentation in NimBLEAdvertising. 2021-08-02 19:51:04 -06:00
h2zero
2e1d78ff78 Code cleanup 2021-08-02 13:29:19 -06:00
h2zero
6be6a111d0 [Server] Implement remove Characteristics/Descriptors. (#54)
This allows for adding and removing of characteristics and descriptors after initial setup of the server.
When used it will send a service changed notification to all clients.
The changes will not take effect until all clients have disconnected and advertising restarted.
2021-07-30 20:56:52 -06:00
h2zero
0a2714c169 [Server] Add callback for MTU change. (#55)
onMTUChange callback added that is called when a connection MTU is updated.
2021-07-19 21:47:59 -06:00
h2zero
7d01fa595d Fix compilation for ESP32C3. (#53) 2021-07-19 21:46:30 -06:00
h2zero
2decc0682a Remove FreeRTOS from CmakeLists. 2021-07-14 10:30:41 -06:00
h2zero
6ff1a49dd5 Update documentation. 2021-07-12 13:33:56 -06:00
h2zero
09adf86036 Remove FreeRTOS files. 2021-07-12 10:25:32 -06:00
h2zero
d3a0f95aaf Add whitelist API.
This adds the functions:
- bool NimBLEDevice::whiteListAdd(const NimBLEAddress & address);
- bool NimBLEDevice::whiteListRemove(const NimBLEAddress & address);
- bool NimBLEDevice::onWhiteList(const NimBLEAddress & address);
- size_t NimBLEDevice::getWhiteListCount();
- NimBLEAddress NimBLEDevice::getWhiteListAddress(size_t index);
2021-05-23 13:12:44 -06:00
h2zero
d9d794ae40 [Server] Add ability to create static attributes.
This allows creating instances of services/characteristics and decriptors without
needing to call the parent createX() methods. This allows creation of the attibutes
within a class or other structure and the attributes can be linked to the parent by
calling the parent addX() method.

Additonally this increases the backward compatibility with the original bluedroid library
to further support user migration.
2021-05-23 13:07:00 -06:00
boozer2
30d6c399b8 [Client] Fix for truncation of strings with null in writeValue std::string overload 2021-05-21 19:54:08 -06:00
Bascy
7815d89dbf Call onSubscribe() after adding connection to subscribedVector
This allows for sending BLE messages in the onSubscribe() handler
2021-05-19 22:28:30 -06:00
Ketan Padegaonkar
9fa9531e50 Add method to get the callbacks from a characteristic.
This is useul to be able to unit/integration test the behavior of a
characteristic.
2021-05-19 22:23:40 -06:00
h2zero
946b971750 Properly find the remote characteristic end handle for descriptor discovery.
Previously we used the service end handle or, if available, the handle of the next characteristic in the service
as the end handle when retrieving the descriptors of a characteristic. This process would fail when connecting to some
devices if the characteristics were retrieved individually instead of all together. The cause of failure was requesting
a handle range that is out of characteristic scope which is also out of line with BLE
specifications.

The fix included in this is to set the end handles of the characteristics either after retrieving all the characteristics
in a service by using the next charactertistic definition handle or, if the characteristic was retrieved separately,
we retrieve the next characteristic just before retrieving the descriptors.
2021-05-17 14:37:03 -06:00
h2zero
b62358a520 Remove task notification for server indications.
This resolves an issue when sending an indication from a callback that can cause the server to hang.
2021-05-17 14:08:02 -06:00
h2zero
e45fb8616a Cleanup logs. 2021-05-15 09:52:00 -06:00
h2zero
d38865e022 Remove some asserts. 2021-05-07 11:06:45 -06:00
h2zero
62d1f67d8b Add connection info class and access methods to server and client.
This adds the ability to access information about the current connection.
A new class was created to wrap the struct ble_gap_conn_desc with methods to retrieve the connection information.

Example server use:
```
for(auto i=0; i<pServer->getConnectedCount();i++) {
    NimBLEConnInfo connInfo = pServer->getPeerInfo(i);
    printf("Connected client %d info:\n", i);
    printf("Peer address: %s\n", connInfo.getAddress().toString().c_str());
    printf("Peer ID address: %s\n", connInfo.getIdAddress().toString().c_str());
    printf("Handle: %u\n", connInfo.getConnHandle());
    printf("Interval: %u\n", connInfo.getConnInterval());
    printf("Timeout: %u\n", connInfo.getConnTimeout());
    printf("Latency: %u\n", connInfo.getConnLatency());
    printf("MTU: %u\n", connInfo.getMTU());
    printf("Master: %s\n", connInfo.isMaster()? "true":"false");
    printf("Slave: %s\n", connInfo.isSlave()? "true":"false");
    printf("Bonded: %s\n", connInfo.isBonded()? "true":"false");
    printf("Authenticated: %s\n", connInfo.isAuthenticated()? "true":"false");
    printf("Encrypted: %s\n", connInfo.isEncrypted()? "true":"false");
    printf("Encryption Key Size: %u\n", connInfo.getSecKeySize());
}
```

Example client use:
```
if (pClient->isConnected()) {
    NimBLEConnInfo connInfo = pClient->getConnInfo();
    printf("Connection info:\n");
    printf("Peer address: %s\n", connInfo.getAddress().toString().c_str());
    printf("Peer ID address: %s\n", connInfo.getIdAddress().toString().c_str());
    printf("Handle: %u\n", connInfo.getConnHandle());
    printf("Interval: %u\n", connInfo.getConnInterval());
    printf("Timeout: %u\n", connInfo.getConnTimeout());
    printf("Latency: %u\n", connInfo.getConnLatency());
    printf("MTU: %u\n", connInfo.getMTU());
    printf("Master: %s\n", connInfo.isMaster()? "true":"false");
    printf("Slave: %s\n", connInfo.isSlave()? "true":"false");
    printf("Bonded: %s\n", connInfo.isBonded()? "true":"false");
    printf("Authenticated: %s\n", connInfo.isAuthenticated()? "true":"false");
    printf("Encrypted: %s\n", connInfo.isEncrypted()? "true":"false");
    printf("Encryption Key Size: %u\n", connInfo.getSecKeySize());
}
```
2021-05-07 09:02:43 -06:00
h2zero
4f8342e275 Add bond management API.
* Adds these new methods to NimBLEDevice to manage bonded peers:

- NimBLEDevice::deleteBond(const NimBLEAddress &address);
- NimBLEDevice::getNumBonds();
- NimBLEDevice::isBonded(const NimBLEAddress &address);
- NimBLEDevice::deleteAllBonds();
- NimBLEDevice::getBondedAddress(int index);
2021-04-25 08:06:38 -06:00
h2zero
05080abad4 Add clear scan duplicate filter cache method + clear on start.
This allows for clearing the duplicate filter cache when required and will also clear it
automatically when starting a scan.

This is useful when unexpectedly disconnected from a device and attempting to reconnect.
Previously the scan filter would not report advertisements from a device if it was multi-connectable
and advertising while connected. The result was long delays until the device would be scanned again.
This resolves it by clearing the filter cache on each scan start/clearResults call
and adding a method to NimBLEScan for manually clearing the cache on demand.
2021-03-31 20:24:57 -06:00
h2zero
f5eab87a87 Fix missing data in long write to descriptor. 2021-03-31 17:43:19 -06:00
h2zero
3c33129600 Remove extra Kconfig and add cmakelists for IDF3 2021-03-28 15:06:27 -06:00
David Robertson
3227681476 Use ESP_LOGx macros for logging (#42)
This allows filtering of this components logging when using higher log levels in IDF.
2021-03-28 14:47:30 -06:00
Aron Rubin
cf3227503b Use more stable arduino detection methods (#38)
* Use a more stable arduino detection macro.

* Detection method for both IDF config phases. Newer IDF API and targets.
2021-03-04 21:00:21 -07:00
h2zero
ef049ddf19 Fix compile errors in latest IDF (NimBLE 1.3.0) see (#37).
Const qualifiers were added to multiple struct members in NimBLE 1.3.0.
This solves compilation errors related to those.
2021-02-10 10:54:53 -07:00
36 changed files with 1056 additions and 709 deletions

View File

@@ -2,6 +2,52 @@
All notable changes to this project will be documented in this file.
## [1.3.1] - 2021-08-04
### Fixed
- Corrected a compiler/linker error when an application or a library uses bluetooth classic due to the redefinition of `btInUse`.
## [1.3.0] - 2021-08-02
### Added
- `NimBLECharacteristic::removeDescriptor`: Dynamically remove a descriptor from a characterisic. Takes effect after all connections are closed and sends a service changed indication.
- `NimBLEService::removeCharacteristic`: Dynamically remove a characteristic from a service. Takes effect after all connections are closed and sends a service changed indication
- `NimBLEServerCallbacks::onMTUChange`: This is callback is called when the MTU is updated after connection with a client.
- ESP32C3 support
- Whitelist API:
- `NimBLEDevice::whiteListAdd`: Add a device to the whitelist.
- `NimBLEDevice::whiteListRemove`: Remove a device from the whitelist.
- `NimBLEDevice::onWhiteList`: Check if the device is on the whitelist.
- `NimBLEDevice::getWhiteListCount`: Gets the size of the whitelist
- `NimBLEDevice::getWhiteListAddress`: Get the address of a device on the whitelist by index value.
- Bond management API:
- `NimBLEDevice::getNumBonds`: Gets the number of bonds stored.
- `NimBLEDevice::isBonded`: Checks if the device is bonded.
- `NimBLEDevice::deleteAllBonds`: Deletes all bonds.
- `NimBLEDevice::getBondedAddress`: Gets the address of a bonded device by the index value.
- `NimBLECharacteristic::getCallbacks` to retrieve the current callback handler.
- Connection Information class: `NimBLEConnInfo`.
- `NimBLEScan::clearDuplicateCache`: This can be used to reset the cache of advertised devices so they will be immediately discovered again.
### Changed
- FreeRTOS files have been removed as they are not used by the library.
- Services, characteristics and descriptors can now be created statically and added after.
- Excess logging and some asserts removed.
- Use ESP_LOGx macros to enable using local log level filtering.
### Fixed
- `NimBLECharacteristicCallbacks::onSubscribe` Is now called after the connection is added to the vector.
- Corrected bonding failure when reinitializing the BLE stack.
- Writing to a characterisic with a std::string value now correctly writes values with null characters.
- Retrieving remote descriptors now uses the characterisic end handle correctly.
- Missing data in long writes to remote descriptors.
- Hanging on task notification when sending an indication from the characteristic callback.
- BLE controller memory could be released when using Arduino as a component.
- Complile errors with NimBLE release 1.3.0.
## [1.2.0] - 2021-02-08
### Added

View File

@@ -2,10 +2,32 @@
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
set(SUPPORTED_TARGETS esp32)
idf_build_get_property(__hack_component_targets __COMPONENT_TARGETS)
set(COMPONENT_SRCS
"src/FreeRTOS.cpp"
if("esp-nimble-component" IN_LIST BUILD_COMPONENTS OR "__esp-nimble-component" IN_LIST __hack_component_targets)
list(APPEND ESP_NIMBLE_PRIV_REQUIRES
esp-nimble-component
)
elseif("nimble" IN_LIST BUILD_COMPONENTS OR "__nimble" IN_LIST __hack_component_targets)
list(APPEND ESP_NIMBLE_PRIV_REQUIRES
nimble
)
endif()
if("arduino" IN_LIST BUILD_COMPONENTS OR __hack_component_targets MATCHES "__idf_arduino")
list(APPEND ESP_NIMBLE_PRIV_REQUIRES
arduino
)
endif()
idf_component_register(
REQUIRED_IDF_TARGETS
"esp32"
"esp32s3"
"esp32c3"
INCLUDE_DIRS
"src"
SRCS
"src/NimBLE2904.cpp"
"src/NimBLEAddress.cpp"
"src/NimBLEAdvertisedDevice.cpp"
@@ -27,31 +49,10 @@ set(COMPONENT_SRCS
"src/NimBLEService.cpp"
"src/NimBLEUtils.cpp"
"src/NimBLEUUID.cpp"
)
set(COMPONENT_ADD_INCLUDEDIRS
src
)
set(COMPONENT_PRIV_REQUIRES
nvs_flash
REQUIRES
bt
nvs_flash
PRIV_REQUIRES
${ESP_NIMBLE_PRIV_REQUIRES}
)
if(COMPONENTS MATCHES "esp-nimble-component")
list(APPEND COMPONENT_PRIV_REQUIRES
esp-nimble-component
)
elseif(COMPONENTS MATCHES "nimble")
list(APPEND COMPONENT_PRIV_REQUIRES
nimble
)
endif()
if(COMPONENTS MATCHES "arduino")
list(APPEND COMPONENT_PRIV_REQUIRES
arduino
)
endif()
register_component()

56
CMakeLists.txt_idf3 Normal file
View File

@@ -0,0 +1,56 @@
# The following lines of boilerplate have to be in your project's
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
set(SUPPORTED_TARGETS esp32)
set(COMPONENT_SRCS
"src/NimBLE2904.cpp"
"src/NimBLEAddress.cpp"
"src/NimBLEAdvertisedDevice.cpp"
"src/NimBLEAdvertising.cpp"
"src/NimBLEBeacon.cpp"
"src/NimBLECharacteristic.cpp"
"src/NimBLEClient.cpp"
"src/NimBLEDescriptor.cpp"
"src/NimBLEDevice.cpp"
"src/NimBLEEddystoneTLM.cpp"
"src/NimBLEEddystoneURL.cpp"
"src/NimBLEHIDDevice.cpp"
"src/NimBLERemoteCharacteristic.cpp"
"src/NimBLERemoteDescriptor.cpp"
"src/NimBLERemoteService.cpp"
"src/NimBLEScan.cpp"
"src/NimBLESecurity.cpp"
"src/NimBLEServer.cpp"
"src/NimBLEService.cpp"
"src/NimBLEUtils.cpp"
"src/NimBLEUUID.cpp"
)
set(COMPONENT_ADD_INCLUDEDIRS
src
)
set(COMPONENT_PRIV_REQUIRES
nvs_flash
bt
)
if(COMPONENTS MATCHES "esp-nimble-component")
list(APPEND COMPONENT_PRIV_REQUIRES
esp-nimble-component
)
elseif(COMPONENTS MATCHES "nimble")
list(APPEND COMPONENT_PRIV_REQUIRES
nimble
)
endif()
if(COMPONENTS MATCHES "arduino")
list(APPEND COMPONENT_PRIV_REQUIRES
arduino
)
endif()
register_component()

View File

@@ -1,27 +0,0 @@
menu "ESP-NimBLE-CPP configuration"
config NIMBLE_CPP_ENABLE_RETURN_CODE_TEXT
bool "Show NimBLE return codes as text in debug log."
default "n"
help
Enabling this option will display return code values as text
messages in the debug log. This will use approximately 8kB
of flash memory.
config NIMBLE_CPP_ENABLE_GAP_EVENT_CODE_TEXT
bool "Show NimBLE gap events as text in debug log."
default "n"
help
Enabling this option will display gap event codes as text
messages in the debug log. This will use approximately 1kB
of flash memory.
config NIMBLE_CPP_ENABLE_ADVERTISMENT_TYPE_TEXT
bool "Show advertisment types as text in debug log."
default "n"
help
Enabling this option will display advertisment types recieved
while scanning as text messages in the debug log.
This will use approximately 250 bytes of flash memory.
endmenu

View File

@@ -68,9 +68,3 @@ in your project/CMakeLists.txt after the line `include($ENV{IDF_PATH}/tools/cmak
* [Jeroen88](https://github.com/Jeroen88) for the amazing help debugging and improving the client code.
<br/>
# Todo
- Improve host reset handler
- Implement random address handling
- Implement bond management
- Add Bluetooth Mesh
<br/>

View File

@@ -38,7 +38,7 @@ PROJECT_NAME = "esp-nimble-cpp / NimBLE-Arduino"
# could be handy for archiving the generated documentation or if some version
# control system is used.
PROJECT_NUMBER = 1.2.0
PROJECT_NUMBER = 1.3.0
# Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer a

View File

@@ -69,8 +69,6 @@ For example `BLEServer::createService(SERVICE_UUID)` will work just as it did be
<a name="characteristics"></a>
### Characteristics
The constructor for `(Nim)BLECharacteristic` is now private, so if you currently subclass it to add logic you should switch to use `NimBLEService::createCharacteristic` instead. Any custom processing logic previously in a `BLECharacteristic` subclass should be moved to a `NimBLECharacteristicCallbacks` subclass instead, and passed into `NimBLECharacteristic::setCallbacks`.
`BLEService::createCharacteristic` (`NimBLEService::createCharacteristic`) is used the same way as originally except the properties parameter has changed.
When creating a characteristic the properties are now set with `NIMBLE_PROPERTY::XXXX` instead of `BLECharacteristic::XXXX`.

View File

@@ -41,6 +41,9 @@ class ServerCallbacks: public NimBLEServerCallbacks {
printf("Client disconnected - start advertising\n");
NimBLEDevice::startAdvertising();
};
void onMTUChange(uint16_t MTU, ble_gap_conn_desc* desc) {
printf("MTU updated: %u for connection ID: %u\n", MTU, desc->conn_handle);
};
/********************* Security handled here **********************
****** Note: these are the same return values as defaults ********/

View File

@@ -1,316 +0,0 @@
/*
* FreeRTOS.cpp
*
* Created on: Feb 24, 2017
* Author: kolban
*/
#include "sdkconfig.h"
#include "FreeRTOS.h"
#include "NimBLELog.h"
#include <freertos/FreeRTOS.h> // Include the base FreeRTOS definitions
#include <freertos/task.h> // Include the task definitions
#include <freertos/semphr.h> // Include the semaphore definitions
#include <string>
static const char* LOG_TAG = "FreeRTOS";
/**
* Sleep for the specified number of milliseconds.
* @param[in] ms The period in milliseconds for which to sleep.
*/
void FreeRTOS::sleep(uint32_t ms) {
::vTaskDelay(ms / portTICK_PERIOD_MS);
} // sleep
/**
* Start a new task.
* @param[in] task The function pointer to the function to be run in the task.
* @param[in] taskName A string identifier for the task.
* @param[in] param An optional parameter to be passed to the started task.
* @param[in] stackSize An optional paremeter supplying the size of the stack in which to run the task.
*/
void FreeRTOS::startTask(void task(void*), std::string taskName, void* param, uint32_t stackSize) {
::xTaskCreate(task, taskName.data(), stackSize, param, 5, NULL);
} // startTask
/**
* Delete the task.
* @param[in] pTask An optional handle to the task to be deleted. If not supplied the calling task will be deleted.
*/
void FreeRTOS::deleteTask(TaskHandle_t pTask) {
::vTaskDelete(pTask);
} // deleteTask
/**
* Get the time in milliseconds since the %FreeRTOS scheduler started.
* @return The time in milliseconds since the %FreeRTOS scheduler started.
*/
uint32_t FreeRTOS::getTimeSinceStart() {
return (uint32_t) (xTaskGetTickCount() * portTICK_PERIOD_MS);
} // getTimeSinceStart
/**
* @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 FreeRTOS::Semaphore::wait(std::string owner) {
NIMBLE_LOGD(LOG_TAG, ">> wait: Semaphore waiting: %s for %s", toString().c_str(), owner.c_str());
if (m_usePthreads) {
pthread_mutex_lock(&m_pthread_mutex);
} else {
xSemaphoreTake(m_semaphore, portMAX_DELAY);
}
if (m_usePthreads) {
pthread_mutex_unlock(&m_pthread_mutex);
} else {
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 FreeRTOS::Semaphore::timedWait(std::string owner, uint32_t timeoutMs) {
NIMBLE_LOGD(LOG_TAG, ">> wait: Semaphore waiting: %s for %s", toString().c_str(), owner.c_str());
if (m_usePthreads && timeoutMs != portMAX_DELAY) {
assert(false); // We apparently don't have a timed wait for pthreads.
}
auto ret = pdTRUE;
if (m_usePthreads) {
pthread_mutex_lock(&m_pthread_mutex);
} else {
ret = xSemaphoreTake(m_semaphore, timeoutMs);
}
if (m_usePthreads) {
pthread_mutex_unlock(&m_pthread_mutex);
} else {
xSemaphoreGive(m_semaphore);
}
NIMBLE_LOGD(LOG_TAG, "<< wait: Semaphore %s released: %d", toString().c_str(), ret);
return ret;
} // wait
/**
* @brief Construct a semaphore, the semaphore is given when created.
* @param [in] name A name string to provide debugging support.
*/
FreeRTOS::Semaphore::Semaphore(std::string name) {
m_usePthreads = false; // Are we using pThreads or FreeRTOS?
if (m_usePthreads) {
pthread_mutex_init(&m_pthread_mutex, nullptr);
} else {
//m_semaphore = xSemaphoreCreateMutex();
m_semaphore = xSemaphoreCreateBinary();
xSemaphoreGive(m_semaphore);
}
m_name = name;
m_owner = std::string("<N/A>");
m_value = 0;
}
FreeRTOS::Semaphore::~Semaphore() {
if (m_usePthreads) {
pthread_mutex_destroy(&m_pthread_mutex);
} else {
vSemaphoreDelete(m_semaphore);
}
}
/**
* @brief Give the semaphore.
*/
void FreeRTOS::Semaphore::give() {
NIMBLE_LOGD(LOG_TAG, "Semaphore giving: %s", toString().c_str());
m_owner = std::string("<N/A>");
if (m_usePthreads) {
pthread_mutex_unlock(&m_pthread_mutex);
} else {
xSemaphoreGive(m_semaphore);
}
// #ifdef ARDUINO_ARCH_ESP32
// FreeRTOS::sleep(10);
// #endif
} // 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 FreeRTOS::Semaphore::give(uint32_t value) {
m_value = value;
give();
} // give
/**
* @brief Give a semaphore from an ISR.
*/
void FreeRTOS::Semaphore::giveFromISR() {
BaseType_t higherPriorityTaskWoken;
if (m_usePthreads) {
assert(false);
} else {
xSemaphoreGiveFromISR(m_semaphore, &higherPriorityTaskWoken);
}
} // giveFromISR
/**
* @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 FreeRTOS::Semaphore::take(std::string owner) {
NIMBLE_LOGD(LOG_TAG, "Semaphore taking: %s for %s", toString().c_str(), owner.c_str());
bool rc = false;
if (m_usePthreads) {
pthread_mutex_lock(&m_pthread_mutex);
} else {
rc = ::xSemaphoreTake(m_semaphore, portMAX_DELAY) == pdTRUE;
}
m_owner = owner;
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
/**
* @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 FreeRTOS::Semaphore::take(uint32_t timeoutMs, std::string owner) {
NIMBLE_LOGD(LOG_TAG, "Semaphore taking: %s for %s", toString().c_str(), owner.c_str());
bool rc = false;
if (m_usePthreads) {
assert(false); // We apparently don't have a timed wait for pthreads.
} else {
rc = ::xSemaphoreTake(m_semaphore, timeoutMs / portTICK_PERIOD_MS) == pdTRUE;
}
m_owner = owner;
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
/**
* @brief Create a string representation of the semaphore.
* @return A string representation of the semaphore.
*/
std::string FreeRTOS::Semaphore::toString() {
char hex[9];
std::string res = "name: " + m_name + " (0x";
snprintf(hex, sizeof(hex), "%08x", (uint32_t)m_semaphore);
res += hex;
res += "), owner: " + m_owner;
return res;
} // toString
/**
* @brief Set the name of the semaphore.
* @param [in] name The name of the semaphore.
*/
void FreeRTOS::Semaphore::setName(std::string name) {
m_name = name;
} // setName
/**
* @brief Create a ring buffer.
* @param [in] length The amount of storage to allocate for the ring buffer.
* @param [in] type The type of buffer. One of RINGBUF_TYPE_NOSPLIT, RINGBUF_TYPE_ALLOWSPLIT, RINGBUF_TYPE_BYTEBUF.
*/
#ifdef ESP_IDF_VERSION //Quick hack to detect if using IDF version that replaced ringbuf_type_t
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 0, 0)
Ringbuffer::Ringbuffer(size_t length, RingbufferType_t type) {
#else
Ringbuffer::Ringbuffer(size_t length, ringbuf_type_t type) {
#endif
#else
Ringbuffer::Ringbuffer(size_t length, ringbuf_type_t type) {
#endif
m_handle = ::xRingbufferCreate(length, type);
} // Ringbuffer
Ringbuffer::~Ringbuffer() {
::vRingbufferDelete(m_handle);
} // ~Ringbuffer
/**
* @brief Receive data from the buffer.
* @param [out] size On return, the size of data returned.
* @param [in] wait How long to wait.
* @return A pointer to the storage retrieved.
*/
void* Ringbuffer::receive(size_t* size, TickType_t wait) {
return ::xRingbufferReceive(m_handle, size, wait);
} // receive
/**
* @brief Return an item.
* @param [in] item The item to be returned/released.
*/
void Ringbuffer::returnItem(void* item) {
::vRingbufferReturnItem(m_handle, item);
} // returnItem
/**
* @brief Send data to the buffer.
* @param [in] data The data to place into the buffer.
* @param [in] length The length of data to place into the buffer.
* @param [in] wait How long to wait before giving up. The default is to wait indefinitely.
* @return
*/
bool Ringbuffer::send(void* data, size_t length, TickType_t wait) {
return ::xRingbufferSend(m_handle, data, length, wait) == pdTRUE;
} // send

View File

@@ -1,89 +0,0 @@
/*
* FreeRTOS.h
*
* Created on: Feb 24, 2017
* Author: kolban
*/
#ifndef MAIN_FREERTOS_H_
#define MAIN_FREERTOS_H_
#include <freertos/FreeRTOS.h> // Include the base FreeRTOS definitions.
#include <freertos/task.h> // Include the task definitions.
#include <freertos/semphr.h> // Include the semaphore definitions.
#include <freertos/ringbuf.h> // Include the ringbuffer definitions.
#include <stdint.h>
#include <string>
#include <pthread.h>
/**
* @brief Interface to %FreeRTOS functions.
*/
class FreeRTOS {
public:
static void sleep(uint32_t ms);
static void startTask(void task(void*), std::string taskName, void* param = nullptr, uint32_t stackSize = 2048);
static void deleteTask(TaskHandle_t pTask = nullptr);
static uint32_t getTimeSinceStart();
/**
* @brief A binary semaphore class that operates like a mutex, it is already given when constructed.
*/
class Semaphore {
public:
Semaphore(std::string owner = "<Unknown>");
~Semaphore();
void give();
void give(uint32_t value);
void giveFromISR();
void setName(std::string name);
bool take(std::string owner = "<Unknown>");
bool take(uint32_t timeoutMs, std::string owner = "<Unknown>");
std::string toString();
bool timedWait(std::string owner = "<Unknown>", uint32_t timeoutMs = portMAX_DELAY);
uint32_t wait(std::string owner = "<Unknown>");
/**
* @brief Get the value of the semaphore.
* @return The value stored if the semaphore was given with give(value);
*/
uint32_t value(){ return m_value; };
private:
SemaphoreHandle_t m_semaphore;
pthread_mutex_t m_pthread_mutex;
std::string m_name;
std::string m_owner;
uint32_t m_value;
bool m_usePthreads;
};
};
/**
* @brief A wrapper class for a freeRTOS ringbuffer.
*/
class Ringbuffer {
public:
#ifdef ESP_IDF_VERSION //Quick hack to detect if using IDF version that replaced ringbuf_type_t
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 0, 0)
Ringbuffer(size_t length, RingbufferType_t type = RINGBUF_TYPE_NOSPLIT);
#else
Ringbuffer(size_t length, ringbuf_type_t type = RINGBUF_TYPE_NOSPLIT);
#endif
#else
Ringbuffer(size_t length, ringbuf_type_t type = RINGBUF_TYPE_NOSPLIT);
#endif
~Ringbuffer();
void* receive(size_t* size, TickType_t wait = portMAX_DELAY);
void returnItem(void* item);
bool send(void* data, size_t length, TickType_t wait = portMAX_DELAY);
private:
RingbufHandle_t m_handle;
};
#endif /* MAIN_FREERTOS_H_ */

View File

@@ -42,6 +42,7 @@ struct BLE2904_Data {
*/
class NimBLE2904: public NimBLEDescriptor {
public:
NimBLE2904(NimBLECharacteristic* pCharacterisitic = nullptr);
static const uint8_t FORMAT_BOOLEAN = 1;
static const uint8_t FORMAT_UINT2 = 2;
static const uint8_t FORMAT_UINT4 = 3;
@@ -77,7 +78,6 @@ public:
void setUnit(uint16_t unit);
private:
NimBLE2904(NimBLECharacteristic* pCharacterisitic);
friend class NimBLECharacteristic;
BLE2904_Data m_data;
}; // BLE2904

View File

@@ -734,7 +734,7 @@ uint8_t* NimBLEAdvertisedDevice::getPayload() {
* @param [in] length The length of the payload in bytes.
* @param [in] append Indicates if the the data should be appended (scan response).
*/
void NimBLEAdvertisedDevice::setPayload(uint8_t *payload, uint8_t length, bool append) {
void NimBLEAdvertisedDevice::setPayload(const uint8_t *payload, uint8_t length, bool append) {
if(!append) {
m_advLength = length;
m_payload.assign(payload, payload + length);

View File

@@ -137,7 +137,7 @@ private:
void setAddress(NimBLEAddress address);
void setAdvType(uint8_t advType);
void setPayload(uint8_t *payload, uint8_t length, bool append);
void setPayload(const uint8_t *payload, uint8_t length, bool append);
void setRSSI(int rssi);
uint8_t findAdvField(uint8_t type, uint8_t index = 0, uint8_t *data_loc = nullptr);
uint8_t findServiceData(uint8_t index, uint8_t* bytes);

View File

@@ -383,6 +383,7 @@ void NimBLEAdvertising::setScanResponseData(NimBLEAdvertisementData& advertiseme
* @brief Start advertising.
* @param [in] duration The duration, in seconds, to advertise, 0 == advertise forever.
* @param [in] advCompleteCB A pointer to a callback to be invoked when advertising ends.
* @return True if advertising started successfully.
*/
bool NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdvertising *pAdv)) {
NIMBLE_LOGD(LOG_TAG, ">> Advertising start: customAdvData: %d, customScanResponseData: %d",
@@ -471,16 +472,14 @@ bool NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdv
}
payloadLen += add;
if(nullptr == (m_advData.uuids16 = (ble_uuid16_t*)realloc(m_advData.uuids16,
if(nullptr == (m_advData.uuids16 = (ble_uuid16_t*)realloc((void*)m_advData.uuids16,
(m_advData.num_uuids16 + 1) * sizeof(ble_uuid16_t))))
{
NIMBLE_LOGC(LOG_TAG, "Error, no mem");
abort();
}
memcpy(&m_advData.uuids16[m_advData.num_uuids16].value,
&it.getNative()->u16.value, 2);
m_advData.uuids16[m_advData.num_uuids16].u.type = BLE_UUID_TYPE_16;
memcpy((void*)&m_advData.uuids16[m_advData.num_uuids16],
&it.getNative()->u16, sizeof(ble_uuid16_t));
m_advData.uuids16_is_complete = 1;
m_advData.num_uuids16++;
}
@@ -492,16 +491,14 @@ bool NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdv
}
payloadLen += add;
if(nullptr == (m_advData.uuids32 = (ble_uuid32_t*)realloc(m_advData.uuids32,
if(nullptr == (m_advData.uuids32 = (ble_uuid32_t*)realloc((void*)m_advData.uuids32,
(m_advData.num_uuids32 + 1) * sizeof(ble_uuid32_t))))
{
NIMBLE_LOGC(LOG_TAG, "Error, no mem");
abort();
}
memcpy(&m_advData.uuids32[m_advData.num_uuids32].value,
&it.getNative()->u32.value, 4);
m_advData.uuids32[m_advData.num_uuids32].u.type = BLE_UUID_TYPE_32;
memcpy((void*)&m_advData.uuids32[m_advData.num_uuids32],
&it.getNative()->u32, sizeof(ble_uuid32_t));
m_advData.uuids32_is_complete = 1;
m_advData.num_uuids32++;
}
@@ -513,16 +510,14 @@ bool NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdv
}
payloadLen += add;
if(nullptr == (m_advData.uuids128 = (ble_uuid128_t*)realloc(m_advData.uuids128,
if(nullptr == (m_advData.uuids128 = (ble_uuid128_t*)realloc((void*)m_advData.uuids128,
(m_advData.num_uuids128 + 1) * sizeof(ble_uuid128_t))))
{
NIMBLE_LOGC(LOG_TAG, "Error, no mem");
abort();
}
memcpy(&m_advData.uuids128[m_advData.num_uuids128].value,
&it.getNative()->u128.value, 16);
m_advData.uuids128[m_advData.num_uuids128].u.type = BLE_UUID_TYPE_128;
memcpy((void*)&m_advData.uuids128[m_advData.num_uuids128],
&it.getNative()->u128, sizeof(ble_uuid128_t));
m_advData.uuids128_is_complete = 1;
m_advData.num_uuids128++;
}
@@ -599,19 +594,19 @@ bool NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdv
}
if(m_advData.num_uuids128 > 0) {
free(m_advData.uuids128);
free((void*)m_advData.uuids128);
m_advData.uuids128 = nullptr;
m_advData.num_uuids128 = 0;
}
if(m_advData.num_uuids32 > 0) {
free(m_advData.uuids32);
free((void*)m_advData.uuids32);
m_advData.uuids32 = nullptr;
m_advData.num_uuids32 = 0;
}
if(m_advData.num_uuids16 > 0) {
free(m_advData.uuids16);
free((void*)m_advData.uuids16);
m_advData.uuids16 = nullptr;
m_advData.num_uuids16 = 0;
}

View File

@@ -45,15 +45,15 @@ NimBLECharacteristic::NimBLECharacteristic(const char* uuid, uint16_t properties
* @param [in] pService - pointer to the service instance this characteristic belongs to.
*/
NimBLECharacteristic::NimBLECharacteristic(const NimBLEUUID &uuid, uint16_t properties, NimBLEService* pService) {
m_uuid = uuid;
m_handle = NULL_HANDLE;
m_properties = properties;
m_pCallbacks = &defaultCallback;
m_pService = pService;
m_value = "";
m_valMux = portMUX_INITIALIZER_UNLOCKED;
m_pTaskData = nullptr;
m_timestamp = 0;
m_uuid = uuid;
m_handle = NULL_HANDLE;
m_properties = properties;
m_pCallbacks = &defaultCallback;
m_pService = pService;
m_value = "";
m_valMux = portMUX_INITIALIZER_UNLOCKED;
m_timestamp = 0;
m_removed = 0;
} // NimBLECharacteristic
/**
@@ -95,9 +95,62 @@ NimBLEDescriptor* NimBLECharacteristic::createDescriptor(const NimBLEUUID &uuid,
pDescriptor = new NimBLEDescriptor(uuid, properties, max_len, this);
}
m_dscVec.push_back(pDescriptor);
addDescriptor(pDescriptor);
return pDescriptor;
} // createCharacteristic
} // createDescriptor
/**
* @brief Add a descriptor to the characteristic.
* @param [in] pDescriptor A pointer to the descriptor to add.
*/
void NimBLECharacteristic::addDescriptor(NimBLEDescriptor *pDescriptor) {
bool foundRemoved = false;
if(pDescriptor->m_removed > 0) {
for(auto& it : m_dscVec) {
if(it == pDescriptor) {
foundRemoved = true;
pDescriptor->m_removed = 0;
}
}
}
if(!foundRemoved) {
m_dscVec.push_back(pDescriptor);
}
pDescriptor->setCharacteristic(this);
NimBLEDevice::getServer()->serviceChanged();
}
/**
* @brief Remove a descriptor from the characterisitc.
* @param[in] pDescriptor A pointer to the descriptor instance to remove from the characterisitc.
* @param[in] deleteDsc If true it will delete the descriptor instance and free it's resources.
*/
void NimBLECharacteristic::removeDescriptor(NimBLEDescriptor *pDescriptor, bool deleteDsc) {
// Check if the descriptor was already removed and if so, check if this
// is being called to delete the object and do so if requested.
// Otherwise, ignore the call and return.
if(pDescriptor->m_removed > 0) {
if(deleteDsc) {
for(auto it = m_dscVec.begin(); it != m_dscVec.end(); ++it) {
if ((*it) == pDescriptor) {
delete *it;
m_dscVec.erase(it);
break;
}
}
}
return;
}
pDescriptor->m_removed = deleteDsc ? NIMBLE_ATT_REMOVE_DELETE : NIMBLE_ATT_REMOVE_HIDE;
NimBLEDevice::getServer()->serviceChanged();
} // removeDescriptor
/**
@@ -165,6 +218,11 @@ NimBLEService* NimBLECharacteristic::getService() {
} // getService
void NimBLECharacteristic::setService(NimBLEService *pService) {
m_pService = pService;
}
/**
* @brief Get the UUID of the characteristic.
* @return The UUID of the characteristic.
@@ -301,16 +359,13 @@ void NimBLECharacteristic::setSubscribe(struct ble_gap_event *event) {
subVal |= NIMBLE_SUB_INDICATE;
}
if(m_pTaskData != nullptr) {
m_pTaskData->rc = (subVal & NIMBLE_SUB_INDICATE) ? 0 :
NimBLECharacteristicCallbacks::Status::ERROR_INDICATE_DISABLED;
xTaskNotifyGive(m_pTaskData->task);
}
NIMBLE_LOGI(LOG_TAG, "New subscribe value for conn: %d val: %d",
event->subscribe.conn_handle, subVal);
event->subscribe.conn_handle, subVal);
if(!event->subscribe.cur_indicate && event->subscribe.prev_indicate) {
NimBLEDevice::getServer()->clearIndicateWait(event->subscribe.conn_handle);
}
m_pCallbacks->onSubscribe(this, &desc, subVal);
auto it = m_subscribedVec.begin();
for(;it != m_subscribedVec.end(); ++it) {
@@ -322,16 +377,14 @@ void NimBLECharacteristic::setSubscribe(struct ble_gap_event *event) {
if(subVal > 0) {
if(it == m_subscribedVec.end()) {
m_subscribedVec.push_back({event->subscribe.conn_handle, subVal});
return;
} else {
(*it).second = subVal;
}
(*it).second = subVal;
} else if(it != m_subscribedVec.end()) {
m_subscribedVec.erase(it);
m_subscribedVec.shrink_to_fit();
}
m_pCallbacks->onSubscribe(this, &desc, subVal);
}
@@ -416,40 +469,20 @@ void NimBLECharacteristic::notify(bool is_notification) {
// We also must create it in each loop iteration because it is consumed with each host call.
os_mbuf *om = ble_hs_mbuf_from_flat((uint8_t*)value.data(), length);
NimBLECharacteristicCallbacks::Status statusRC;
if(!is_notification && (m_properties & NIMBLE_PROPERTY::INDICATE)) {
ble_task_data_t taskData = {nullptr, xTaskGetCurrentTaskHandle(),0, nullptr};
m_pTaskData = &taskData;
if(!NimBLEDevice::getServer()->setIndicateWait(it.first)) {
NIMBLE_LOGE(LOG_TAG, "prior Indication in progress");
os_mbuf_free_chain(om);
return;
}
rc = ble_gattc_indicate_custom(it.first, m_handle, om);
if(rc != 0){
statusRC = NimBLECharacteristicCallbacks::Status::ERROR_GATT;
} else {
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
rc = m_pTaskData->rc;
}
m_pTaskData = nullptr;
if(rc == BLE_HS_EDONE) {
rc = 0;
statusRC = NimBLECharacteristicCallbacks::Status::SUCCESS_INDICATE;
} else if(rc == BLE_HS_ETIMEOUT) {
statusRC = NimBLECharacteristicCallbacks::Status::ERROR_INDICATE_TIMEOUT;
} else {
statusRC = NimBLECharacteristicCallbacks::Status::ERROR_INDICATE_FAILURE;
NimBLEDevice::getServer()->clearIndicateWait(it.first);
}
} else {
rc = ble_gattc_notify_custom(it.first, m_handle, om);
if(rc == 0) {
statusRC = NimBLECharacteristicCallbacks::Status::SUCCESS_NOTIFY;
} else {
statusRC = NimBLECharacteristicCallbacks::Status::ERROR_GATT;
}
ble_gattc_notify_custom(it.first, m_handle, om);
}
m_pCallbacks->onStatus(this, statusRC, rc);
}
NIMBLE_LOGD(LOG_TAG, "<< notify");
@@ -469,6 +502,13 @@ void NimBLECharacteristic::setCallbacks(NimBLECharacteristicCallbacks* pCallback
}
} // setCallbacks
/**
* @brief Get the callback handlers for this characteristic.
*/
NimBLECharacteristicCallbacks* NimBLECharacteristic::getCallbacks() {
return m_pCallbacks;
} //getCallbacks
/**
* @brief Set the value of the characteristic.

View File

@@ -59,12 +59,26 @@ class NimBLECharacteristicCallbacks;
*/
class NimBLECharacteristic {
public:
NimBLECharacteristic(const char* uuid,
uint16_t properties =
NIMBLE_PROPERTY::READ |
NIMBLE_PROPERTY::WRITE,
NimBLEService* pService = nullptr);
NimBLECharacteristic(const NimBLEUUID &uuid,
uint16_t properties =
NIMBLE_PROPERTY::READ |
NIMBLE_PROPERTY::WRITE,
NimBLEService* pService = nullptr);
~NimBLECharacteristic();
uint16_t getHandle();
NimBLEUUID getUUID();
std::string toString();
void setCallbacks(NimBLECharacteristicCallbacks* pCallbacks);
NimBLECharacteristicCallbacks*
getCallbacks();
void indicate();
void notify(bool is_notification = true);
@@ -81,9 +95,11 @@ public:
NIMBLE_PROPERTY::WRITE,
uint16_t max_len = 100);
void addDescriptor(NimBLEDescriptor *pDescriptor);
NimBLEDescriptor* getDescriptorByUUID(const char* uuid);
NimBLEDescriptor* getDescriptorByUUID(const NimBLEUUID &uuid);
NimBLEDescriptor* getDescriptorByHandle(uint16_t handle);
void removeDescriptor(NimBLEDescriptor *pDescriptor, bool deleteDsc = false);
std::string getValue(time_t *timestamp = nullptr);
size_t getDataLength();
@@ -115,30 +131,15 @@ public:
setValue((uint8_t*)&s, sizeof(T));
}
NimBLEService* getService();
uint16_t getProperties();
private:
friend class NimBLEServer;
friend class NimBLEService;
friend class NimBLEServer;
friend class NimBLEService;
NimBLECharacteristic(const char* uuid,
uint16_t properties =
NIMBLE_PROPERTY::READ |
NIMBLE_PROPERTY::WRITE,
NimBLEService* pService = nullptr);
NimBLECharacteristic(const NimBLEUUID &uuid,
uint16_t properties =
NIMBLE_PROPERTY::READ |
NIMBLE_PROPERTY::WRITE,
NimBLEService* pService = nullptr);
~NimBLECharacteristic();
NimBLEService* getService();
uint16_t getProperties();
void setService(NimBLEService *pService);
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);
@@ -150,9 +151,9 @@ private:
NimBLEService* m_pService;
std::string m_value;
std::vector<NimBLEDescriptor*> m_dscVec;
ble_task_data_t *m_pTaskData;
portMUX_TYPE m_valMux;
time_t m_timestamp;
uint8_t m_removed;
std::vector<std::pair<uint16_t, uint16_t>> m_subscribedVec;
}; // NimBLECharacteristic

View File

@@ -436,6 +436,24 @@ void NimBLEClient::updateConnParams(uint16_t minInterval, uint16_t maxInterval,
} // updateConnParams
/**
* @brief Get detailed information about the current peer connection.
*/
NimBLEConnInfo NimBLEClient::getConnInfo() {
NimBLEConnInfo connInfo;
if (!isConnected()) {
NIMBLE_LOGE(LOG_TAG, "Not connected");
} else {
int rc = ble_gap_conn_find(m_conn_id, &connInfo.m_desc);
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "Connection info not found");
}
}
return connInfo;
} // getConnInfo
/**
* @brief Set the timeout to wait for connection attempt to complete.
* @param [in] time The number of seconds before timeout.

View File

@@ -23,6 +23,7 @@
#include "NimBLEAddress.h"
#include "NimBLEUUID.h"
#include "NimBLEUtils.h"
#include "NimBLEConnInfo.h"
#include "NimBLEAdvertisedDevice.h"
#include "NimBLERemoteService.h"
@@ -71,6 +72,7 @@ public:
void updateConnParams(uint16_t minInterval, uint16_t maxInterval,
uint16_t latency, uint16_t timeout);
void discoverAttributes();
NimBLEConnInfo getConnInfo();
private:
NimBLEClient(const NimBLEAddress &peerAddress);

55
src/NimBLEConnInfo.h Normal file
View File

@@ -0,0 +1,55 @@
#ifndef NIMBLECONNINFO_H_
#define NIMBLECONNINFO_H_
#include "NimBLEAddress.h"
/**
* @brief Connection information.
*/
class NimBLEConnInfo {
friend class NimBLEServer;
friend class NimBLEClient;
ble_gap_conn_desc m_desc;
NimBLEConnInfo() { m_desc = {}; }
NimBLEConnInfo(ble_gap_conn_desc desc) { m_desc = desc; }
public:
/** @brief Gets the over-the-air address of the connected peer */
NimBLEAddress getAddress() { return NimBLEAddress(m_desc.peer_ota_addr); }
/** @brief Gets the ID address of the connected peer */
NimBLEAddress getIdAddress() { return NimBLEAddress(m_desc.peer_id_addr); }
/** @brief Gets the connection handle of the connected peer */
uint16_t getConnHandle() { return m_desc.conn_handle; }
/** @brief Gets the connection interval for this connection (in 1.25ms units) */
uint16_t getConnInterval() { return m_desc.conn_itvl; }
/** @brief Gets the supervision timeout for this connection (in 10ms units) */
uint16_t getConnTimeout() { return m_desc.supervision_timeout; }
/** @brief Gets the allowable latency for this connection (unit = number of intervals) */
uint16_t getConnLatency() { return m_desc.conn_latency; }
/** @brief Gets the maximum transmission unit size for this connection (in bytes) */
uint16_t getMTU() { return ble_att_mtu(m_desc.conn_handle); }
/** @brief Check if we are in the master role in this connection */
bool isMaster() { return (m_desc.role == BLE_GAP_ROLE_MASTER); }
/** @brief Check if we are in the slave role in this connection */
bool isSlave() { return (m_desc.role == BLE_GAP_ROLE_SLAVE); }
/** @brief Check if we are connected to a bonded peer */
bool isBonded() { return (m_desc.sec_state.bonded == 1); }
/** @brief Check if the connection in encrypted */
bool isEncrypted() { return (m_desc.sec_state.encrypted == 1); }
/** @brief Check if the the connection has been authenticated */
bool isAuthenticated() { return (m_desc.sec_state.authenticated == 1); }
/** @brief Gets the key size used to encrypt the connection */
uint8_t getSecKeySize() { return m_desc.sec_state.key_size; }
};
#endif

View File

@@ -37,6 +37,7 @@ NimBLEDescriptor::NimBLEDescriptor(const char* uuid, uint16_t properties, uint16
: NimBLEDescriptor(NimBLEUUID(uuid), max_len, properties, pCharacteristic) {
}
/**
* @brief NimBLEDescriptor constructor.
*/
@@ -47,11 +48,12 @@ NimBLEDescriptor::NimBLEDescriptor(NimBLEUUID uuid, uint16_t properties, uint16_
m_value.attr_len = 0; // Initial length is 0.
m_value.attr_max_len = max_len; // Maximum length of the data.
m_handle = NULL_HANDLE; // Handle is initially unknown.
m_pCharacteristic = nullptr; // No initial characteristic.
m_pCharacteristic = pCharacteristic;
m_pCallbacks = &defaultCallbacks; // No initial callback.
m_value.attr_value = (uint8_t*) calloc(max_len,1); // Allocate storage for the value.
m_valMux = portMUX_INITIALIZER_UNLOCKED;
m_properties = 0;
m_removed = 0;
if (properties & BLE_GATT_CHR_F_READ) { // convert uint16_t properties to uint8_t
m_properties |= BLE_ATT_F_READ;
@@ -122,6 +124,7 @@ uint8_t* NimBLEDescriptor::getValue() {
return m_value.attr_value;
} // getValue
/**
* @brief Get the value of this descriptor as a string.
* @return A std::string instance containing a copy of the descriptor's value.
@@ -130,9 +133,18 @@ std::string NimBLEDescriptor::getStringValue() {
return std::string((char *) m_value.attr_value, m_value.attr_len);
}
/**
* @brief Get the characteristic this descriptor belongs to.
* @return A pointer to the characteristic this descriptor belongs to.
*/
NimBLECharacteristic* NimBLEDescriptor::getCharacteristic() {
return m_pCharacteristic;
} // getCharacteristic
int NimBLEDescriptor::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) {
const ble_uuid_t *uuid;
int rc;
NimBLEDescriptor* pDescriptor = (NimBLEDescriptor*)arg;
@@ -169,7 +181,7 @@ int NimBLEDescriptor::handleGapEvent(uint16_t conn_handle, uint16_t attr_handle,
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);
memcpy(&buf[len], next->om_data, next->om_len);
len += next->om_len;
next = SLIST_NEXT(next, om_next);
}
@@ -237,6 +249,14 @@ void NimBLEDescriptor::setValue(const std::string &value) {
setValue((uint8_t*) value.data(), value.length());
} // setValue
/**
* @brief Set the characteristic this descriptor belongs to.
* @param [in] pChar A pointer to the characteristic this descriptior belongs to.
*/
void NimBLEDescriptor::setCharacteristic(NimBLECharacteristic* pChar) {
m_pCharacteristic = pChar;
} // setCharacteristic
/**
* @brief Return a string representation of the descriptor.

View File

@@ -43,46 +43,48 @@ class NimBLEDescriptorCallbacks;
*/
class NimBLEDescriptor {
public:
uint16_t getHandle();
NimBLEUUID getUUID();
std::string toString();
NimBLEDescriptor(const char* uuid, uint16_t properties,
uint16_t max_len,
NimBLECharacteristic* pCharacteristic = nullptr);
void setCallbacks(NimBLEDescriptorCallbacks* pCallbacks);
NimBLEDescriptor(NimBLEUUID uuid, uint16_t properties,
uint16_t max_len,
NimBLECharacteristic* pCharacteristic = nullptr);
size_t getLength();
uint8_t* getValue();
std::string getStringValue();
~NimBLEDescriptor();
uint16_t getHandle();
NimBLEUUID getUUID();
std::string toString();
void setCallbacks(NimBLEDescriptorCallbacks* pCallbacks);
size_t getLength();
uint8_t* getValue();
std::string getStringValue();
void setValue(const uint8_t* data, size_t size);
void setValue(const std::string &value);
NimBLECharacteristic* getCharacteristic();
void setValue(const uint8_t* data, size_t size);
void setValue(const std::string &value);
/**
* @brief Convenience template to set the descriptor value to <type\>val.
* @param [in] s The value to set.
*/
template<typename T>
void setValue(const T &s) {
void setValue(const T &s) {
setValue((uint8_t*)&s, sizeof(T));
}
private:
friend class NimBLECharacteristic;
friend class NimBLEService;
friend class NimBLE2902;
friend class NimBLE2904;
NimBLEDescriptor(const char* uuid, uint16_t properties,
uint16_t max_len,
NimBLECharacteristic* pCharacteristic);
NimBLEDescriptor(NimBLEUUID uuid, uint16_t properties,
uint16_t max_len,
NimBLECharacteristic* pCharacteristic);
~NimBLEDescriptor();
static int handleGapEvent(uint16_t conn_handle, uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt, void *arg);
void setHandle(uint16_t handle);
void setCharacteristic(NimBLECharacteristic* pChar);
NimBLEUUID m_uuid;
uint16_t m_handle;
@@ -91,6 +93,7 @@ private:
uint8_t m_properties;
attr_value_t m_value;
portMUX_TYPE m_valMux;
uint8_t m_removed;
}; // NimBLEDescriptor

View File

@@ -30,7 +30,7 @@
#include "services/gap/ble_svc_gap.h"
#include "services/gatt/ble_svc_gatt.h"
#ifdef ARDUINO_ARCH_ESP32
#ifdef CONFIG_ENABLE_ARDUINO_DEPENDS
#include "esp32-hal-bt.h"
#endif
@@ -60,6 +60,7 @@ ble_gap_event_listener NimBLEDevice::m_listener;
std::list <NimBLEClient*> NimBLEDevice::m_cList;
#endif
std::list <NimBLEAddress> NimBLEDevice::m_ignoreList;
std::vector<NimBLEAddress> NimBLEDevice::m_whiteList;
NimBLESecurityCallbacks* NimBLEDevice::m_securityCallbacks = nullptr;
uint8_t NimBLEDevice::m_own_addr_type = BLE_OWN_ADDR_PUBLIC;
uint16_t NimBLEDevice::m_scanDuplicateSize = CONFIG_BTDM_SCAN_DUPL_CACHE_SIZE;
@@ -448,6 +449,211 @@ void NimBLEDevice::setScanFilterMode(uint8_t mode) {
m_scanFilterMode = mode;
}
#if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL) || defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
/**
* @brief Gets the number of bonded peers stored
*/
/*STATIC*/
int NimBLEDevice::getNumBonds() {
ble_addr_t peer_id_addrs[MYNEWT_VAL(BLE_STORE_MAX_BONDS)];
int num_peers, rc;
rc = ble_store_util_bonded_peers(&peer_id_addrs[0], &num_peers, MYNEWT_VAL(BLE_STORE_MAX_BONDS));
if (rc !=0) {
return 0;
}
return num_peers;
}
/**
* @brief Deletes all bonding information.
*/
/*STATIC*/
void NimBLEDevice::deleteAllBonds() {
ble_store_clear();
}
/**
* @brief Deletes a peer bond.
* @param [in] address The address of the peer with which to delete bond info.
* @returns true on success.
*/
/*STATIC*/
bool NimBLEDevice::deleteBond(const NimBLEAddress &address) {
ble_addr_t delAddr;
memcpy(&delAddr.val, address.getNative(),6);
delAddr.type = address.getType();
int rc = ble_gap_unpair(&delAddr);
if (rc != 0) {
return false;
}
return true;
}
/**
* @brief Checks if a peer device is bonded.
* @param [in] address The address to check for bonding.
* @returns true if bonded.
*/
/*STATIC*/
bool NimBLEDevice::isBonded(const NimBLEAddress &address) {
ble_addr_t peer_id_addrs[MYNEWT_VAL(BLE_STORE_MAX_BONDS)];
int num_peers, rc;
rc = ble_store_util_bonded_peers(&peer_id_addrs[0], &num_peers, MYNEWT_VAL(BLE_STORE_MAX_BONDS));
if (rc != 0) {
return false;
}
for (int i = 0; i < num_peers; i++) {
NimBLEAddress storedAddr(peer_id_addrs[i]);
if(storedAddr == address) {
return true;
}
}
return false;
}
/**
* @brief Get the address of a bonded peer device by index.
* @param [in] index The index to retrieve the peer address of.
* @returns NimBLEAddress of the found bonded peer or nullptr if not found.
*/
/*STATIC*/
NimBLEAddress NimBLEDevice::getBondedAddress(int index) {
ble_addr_t peer_id_addrs[MYNEWT_VAL(BLE_STORE_MAX_BONDS)];
int num_peers, rc;
rc = ble_store_util_bonded_peers(&peer_id_addrs[0], &num_peers, MYNEWT_VAL(BLE_STORE_MAX_BONDS));
if (rc != 0) {
return nullptr;
}
if (index > num_peers || index < 0) {
return nullptr;
}
return NimBLEAddress(peer_id_addrs[index]);
}
#endif
/**
* @brief Checks if a peer device is whitelisted.
* @param [in] address The address to check for in the whitelist.
* @returns true if the address is in the whitelist.
*/
bool NimBLEDevice::onWhiteList(const NimBLEAddress & address) {
for (auto &it : m_whiteList) {
if (it == address) {
return true;
}
}
return false;
}
/**
* @brief Add a peer address to the whitelist.
* @param [in] address The address to add to the whitelist.
* @returns true if successful.
*/
bool NimBLEDevice::whiteListAdd(const NimBLEAddress & address) {
if (NimBLEDevice::onWhiteList(address)) {
return true;
}
m_whiteList.push_back(address);
std::vector<ble_addr_t> wlVec;
wlVec.reserve(m_whiteList.size());
for (auto &it : m_whiteList) {
ble_addr_t wlAddr;
memcpy(&wlAddr.val, it.getNative(), 6);
wlAddr.type = it.getType();
wlVec.push_back(wlAddr);
}
int rc = ble_gap_wl_set(&wlVec[0], wlVec.size());
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "Failed adding to whitelist rc=%d", rc);
return false;
}
return true;
}
/**
* @brief Remove a peer address from the whitelist.
* @param [in] address The address to remove from the whitelist.
* @returns true if successful.
*/
bool NimBLEDevice::whiteListRemove(const NimBLEAddress & address) {
if (!NimBLEDevice::onWhiteList(address)) {
return true;
}
std::vector<ble_addr_t> wlVec;
wlVec.reserve(m_whiteList.size());
for (auto &it : m_whiteList) {
if (it != address) {
ble_addr_t wlAddr;
memcpy(&wlAddr.val, it.getNative(), 6);
wlAddr.type = it.getType();
wlVec.push_back(wlAddr);
}
}
int rc = ble_gap_wl_set(&wlVec[0], wlVec.size());
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "Failed removing from whitelist rc=%d", rc);
return false;
}
// Don't remove from the list unless NimBLE returned success
for (auto it = m_whiteList.begin(); it < m_whiteList.end(); ++it) {
if ((*it) == address) {
m_whiteList.erase(it);
break;
}
}
return true;
}
/**
* @brief Gets the count of addresses in the whitelist.
* @returns The number of addresses in the whitelist.
*/
size_t NimBLEDevice::getWhiteListCount() {
return m_whiteList.size();
}
/**
* @brief Gets the address at the vector index.
* @param [in] index The vector index to retrieve the address from.
* @returns the NimBLEAddress at the whitelist index or nullptr if not found.
*/
NimBLEAddress NimBLEDevice::getWhiteListAddress(size_t index) {
if (index > m_whiteList.size()) {
NIMBLE_LOGE(LOG_TAG, "Invalid index; %u", index);
return nullptr;
}
return m_whiteList[index];
}
/**
* @brief Host reset, we pass the message so we don't make calls until resynced.
@@ -534,7 +740,7 @@ void NimBLEDevice::setScanFilterMode(uint8_t mode) {
int rc=0;
esp_err_t errRc = ESP_OK;
#ifdef ARDUINO_ARCH_ESP32
#ifdef CONFIG_ENABLE_ARDUINO_DEPENDS
// make sure the linker includes esp32-hal-bt.c so ardruino init doesn't release BLE memory.
btStarted();
#endif
@@ -551,8 +757,12 @@ void NimBLEDevice::setScanFilterMode(uint8_t mode) {
esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT);
esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
#ifdef CONFIG_IDF_TARGET_ESP32C3
bt_cfg.bluetooth_mode = ESP_BT_MODE_BLE;
#else
bt_cfg.mode = ESP_BT_MODE_BLE;
bt_cfg.ble_max_conn = CONFIG_BT_NIMBLE_MAX_CONNECTIONS;
#endif
bt_cfg.normal_adv_size = m_scanDuplicateSize;
bt_cfg.scan_duplicate_type = m_scanFilterMode;
@@ -771,17 +981,23 @@ void NimBLEDevice::setSecurityCallbacks(NimBLESecurityCallbacks* callbacks) {
void NimBLEDevice::setOwnAddrType(uint8_t own_addr_type, bool useNRPA) {
m_own_addr_type = own_addr_type;
switch (own_addr_type) {
#ifdef CONFIG_IDF_TARGET_ESP32
case BLE_OWN_ADDR_PUBLIC:
ble_hs_pvcy_rpa_config(NIMBLE_HOST_DISABLE_PRIVACY);
break;
#endif
case BLE_OWN_ADDR_RANDOM:
setSecurityInitKey(BLE_SM_PAIR_KEY_DIST_ENC | BLE_SM_PAIR_KEY_DIST_ID);
#ifdef CONFIG_IDF_TARGET_ESP32
ble_hs_pvcy_rpa_config(useNRPA ? NIMBLE_HOST_ENABLE_NRPA : NIMBLE_HOST_ENABLE_RPA);
#endif
break;
case BLE_OWN_ADDR_RPA_PUBLIC_DEFAULT:
case BLE_OWN_ADDR_RPA_RANDOM_DEFAULT:
setSecurityInitKey(BLE_SM_PAIR_KEY_DIST_ENC | BLE_SM_PAIR_KEY_DIST_ID);
#ifdef CONFIG_IDF_TARGET_ESP32
ble_hs_pvcy_rpa_config(NIMBLE_HOST_ENABLE_RPA);
#endif
break;
}
} // setOwnAddrType

View File

@@ -95,6 +95,11 @@ public:
static bool getInitialized();
static NimBLEAddress getAddress();
static std::string toString();
static bool whiteListAdd(const NimBLEAddress & address);
static bool whiteListRemove(const NimBLEAddress & address);
static bool onWhiteList(const NimBLEAddress & address);
static size_t getWhiteListCount();
static NimBLEAddress getWhiteListAddress(size_t index);
#if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
static NimBLEScan* getScan();
@@ -142,6 +147,14 @@ public:
static std::list<NimBLEClient*>* getClientList();
#endif
#if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL) || defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
static bool deleteBond(const NimBLEAddress &address);
static int getNumBonds();
static bool isBonded(const NimBLEAddress &address);
static void deleteAllBonds();
static NimBLEAddress getBondedAddress(int index);
#endif
private:
#if defined( CONFIG_BT_NIMBLE_ROLE_CENTRAL)
friend class NimBLEClient;
@@ -188,6 +201,7 @@ private:
static uint8_t m_own_addr_type;
static uint16_t m_scanDuplicateSize;
static uint8_t m_scanFilterMode;
static std::vector<NimBLEAddress> m_whiteList;
};

View File

@@ -7,17 +7,19 @@
*/
#ifndef MAIN_NIMBLELOG_H_
#define MAIN_NIMBLELOG_H_
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#ifdef ARDUINO_ARCH_ESP32
#include "syscfg/syscfg.h"
#include "modlog/modlog.h"
// If Arduino is being used, strip out the colors and ignore log printing below ui setting.
// Note: because CONFIG_LOG_DEFAULT_LEVEL is set at ERROR in Arduino we must use MODLOG_DFLT(ERROR
// otherwise no messages will be printed above that level.
#ifdef ARDUINO_ARCH_ESP32
#ifndef CORE_DEBUG_LEVEL
#define CORE_DEBUG_LEVEL CONFIG_ARDUHAL_LOG_DEFAULT_LEVEL
#endif
@@ -49,11 +51,15 @@
#define NIMBLE_LOGC( tag, format, ... ) MODLOG_DFLT(CRITICAL, "CRIT %s: "#format"\n",tag,##__VA_ARGS__)
#else
#define NIMBLE_LOGE( tag, format, ... ) MODLOG_DFLT(ERROR, "\033[0;31mE %s: "#format"\033[0m\n",tag,##__VA_ARGS__)
#define NIMBLE_LOGW( tag, format, ... ) MODLOG_DFLT(WARN, "\033[0;33mW %s: "#format"\033[0m\n",tag,##__VA_ARGS__)
#define NIMBLE_LOGI( tag, format, ... ) MODLOG_DFLT(INFO, "\033[0;32mI %s: "#format"\033[0m\n",tag,##__VA_ARGS__)
#define NIMBLE_LOGD( tag, format, ... ) MODLOG_DFLT(DEBUG, "D %s: "#format"\n",tag,##__VA_ARGS__)
#define NIMBLE_LOGC( tag, format, ... ) MODLOG_DFLT(CRITICAL, "\033[1;31mCRIT %s: "#format"\033[0m\n",tag,##__VA_ARGS__)
#include "esp_log.h"
#define NIMBLE_LOGE(tag, format, ...) ESP_LOGE(tag, format, ##__VA_ARGS__)
#define NIMBLE_LOGW(tag, format, ...) ESP_LOGW(tag, format, ##__VA_ARGS__)
#define NIMBLE_LOGI(tag, format, ...) ESP_LOGI(tag, format, ##__VA_ARGS__)
#define NIMBLE_LOGD(tag, format, ...) ESP_LOGD(tag, format, ##__VA_ARGS__)
#define NIMBLE_LOGC(tag, format, ...) ESP_LOGE(tag, format, ##__VA_ARGS__)
#endif /*ARDUINO_ARCH_ESP32*/
#endif /*CONFIG_BT_ENABLED*/

View File

@@ -55,6 +55,7 @@ static const char* LOG_TAG = "NimBLERemoteCharacteristic";
m_handle = chr->val_handle;
m_defHandle = chr->def_handle;
m_endHandle = 0;
m_charProp = chr->properties;
m_pRemoteService = pRemoteService;
m_notifyCallback = nullptr;
@@ -146,31 +147,25 @@ int NimBLERemoteCharacteristic::descriptorDiscCB(uint16_t conn_handle,
const struct ble_gatt_dsc *dsc,
void *arg)
{
NIMBLE_LOGD(LOG_TAG,"Descriptor Discovered >> status: %d handle: %d",
error->status, (error->status == 0) ? dsc->handle : -1);
int rc = error->status;
NIMBLE_LOGD(LOG_TAG, "Descriptor Discovered >> status: %d handle: %d",
rc, (rc == 0) ? dsc->handle : -1);
desc_filter_t *filter = (desc_filter_t*)arg;
const NimBLEUUID *uuid_filter = filter->uuid;
ble_task_data_t *pTaskData = (ble_task_data_t*)filter->task_data;
NimBLERemoteCharacteristic *characteristic = (NimBLERemoteCharacteristic*)pTaskData->pATT;
int rc=0;
if(characteristic->getRemoteService()->getClient()->getConnId() != conn_handle){
if (characteristic->getRemoteService()->getClient()->getConnId() != conn_handle){
return 0;
}
switch (error->status) {
switch (rc) {
case 0: {
if(dsc->uuid.u.type == BLE_UUID_TYPE_16 && dsc->uuid.u16.value == uint16_t(0x2803)) {
NIMBLE_LOGD(LOG_TAG,"Descriptor NOT found - end of Characteristic definintion");
rc = BLE_HS_EDONE;
break;
}
if(uuid_filter != nullptr) {
if(ble_uuid_cmp(&uuid_filter->getNative()->u, &dsc->uuid.u) != 0) {
if (uuid_filter != nullptr) {
if (ble_uuid_cmp(&uuid_filter->getNative()->u, &dsc->uuid.u) != 0) {
return 0;
} else {
NIMBLE_LOGD(LOG_TAG,"Descriptor Found");
rc = BLE_HS_EDONE;
}
}
@@ -180,11 +175,10 @@ int NimBLERemoteCharacteristic::descriptorDiscCB(uint16_t conn_handle,
break;
}
default:
rc = error->status;
break;
}
/** If rc == BLE_HS_EDONE, resume the task with a success error code and stop the discovery process.
/* If rc == BLE_HS_EDONE, resume the task with a success error code and stop the discovery process.
* Else if rc == 0, just return 0 to continue the discovery until we get BLE_HS_EDONE.
* If we get any other error code tell the application to abort by returning non-zero in the rc.
*/
@@ -202,6 +196,38 @@ int NimBLERemoteCharacteristic::descriptorDiscCB(uint16_t conn_handle,
}
/**
* @brief callback from NimBLE when the next characteristic of the service is discovered.
*/
int NimBLERemoteCharacteristic::nextCharCB(uint16_t conn_handle,
const struct ble_gatt_error *error,
const struct ble_gatt_chr *chr, void *arg)
{
int rc = error->status;
NIMBLE_LOGD(LOG_TAG, "Next Characteristic >> status: %d handle: %d",
rc, (rc == 0) ? chr->val_handle : -1);
ble_task_data_t *pTaskData = (ble_task_data_t*)arg;
NimBLERemoteCharacteristic *pChar = (NimBLERemoteCharacteristic*)pTaskData->pATT;
if (pChar->getRemoteService()->getClient()->getConnId() != conn_handle) {
return 0;
}
if (rc == 0) {
pChar->m_endHandle = chr->def_handle - 1;
rc = BLE_HS_EDONE;
} else if (rc == BLE_HS_EDONE) {
pChar->m_endHandle = pChar->getRemoteService()->getEndHandle();
} else {
pTaskData->rc = rc;
}
xTaskNotifyGive(pTaskData->task);
return rc;
}
/**
* @brief Populate the descriptors (if any) for this characteristic.
* @param [in] the end handle of the characteristic, or the service, whichever comes first.
@@ -209,35 +235,57 @@ int NimBLERemoteCharacteristic::descriptorDiscCB(uint16_t conn_handle,
bool NimBLERemoteCharacteristic::retrieveDescriptors(const NimBLEUUID *uuid_filter) {
NIMBLE_LOGD(LOG_TAG, ">> retrieveDescriptors() for characteristic: %s", getUUID().toString().c_str());
uint16_t endHandle = getRemoteService()->getEndHandle(this);
if(m_handle >= endHandle) {
return false;
// If this is the last handle then there are no descriptors
if (m_handle == getRemoteService()->getEndHandle()) {
return true;
}
int rc = 0;
ble_task_data_t taskData = {this, xTaskGetCurrentTaskHandle(), 0, nullptr};
// If we don't know the end handle of this characteristic retrieve the next one in the service
// The end handle is the next characteristic definition handle -1.
if (m_endHandle == 0) {
rc = ble_gattc_disc_all_chrs(getRemoteService()->getClient()->getConnId(),
m_handle,
getRemoteService()->getEndHandle(),
NimBLERemoteCharacteristic::nextCharCB,
&taskData);
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "Error getting end handle rc=%d", rc);
return false;
}
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
if (taskData.rc != 0) {
NIMBLE_LOGE(LOG_TAG, "Could not retrieve end handle rc=%d", taskData.rc);
return false;
}
}
desc_filter_t filter = {uuid_filter, &taskData};
rc = ble_gattc_disc_all_dscs(getRemoteService()->getClient()->getConnId(),
m_handle,
endHandle,
m_endHandle,
NimBLERemoteCharacteristic::descriptorDiscCB,
&filter);
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "ble_gattc_disc_all_chrs: rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc));
NIMBLE_LOGE(LOG_TAG, "ble_gattc_disc_all_dscs: rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc));
return false;
}
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
if(taskData.rc != 0) {
NIMBLE_LOGE(LOG_TAG, "ble_gattc_disc_all_chrs: startHandle:%d endHandle:%d taskData.rc=%d %s", m_handle, endHandle, taskData.rc, NimBLEUtils::returnCodeToString(0x0100+taskData.rc));
return false;
if (taskData.rc != 0) {
NIMBLE_LOGE(LOG_TAG, "Failed to retrieve descriptors; startHandle:%d endHandle:%d taskData.rc=%d",
m_handle, m_endHandle, taskData.rc);
}
return true;
NIMBLE_LOGD(LOG_TAG, "<< retrieveDescriptors(): Found %d descriptors.", m_descriptorVector.size());
return (taskData.rc == 0);
} // retrieveDescriptors
@@ -664,7 +712,7 @@ std::string NimBLERemoteCharacteristic::toString() {
* @return false if not connected or cant perform write for some reason.
*/
bool NimBLERemoteCharacteristic::writeValue(const std::string &newValue, bool response) {
return writeValue((uint8_t*)newValue.c_str(), strlen(newValue.c_str()), response);
return writeValue((uint8_t*)newValue.c_str(), newValue.length(), response);
} // writeValue

View File

@@ -148,12 +148,15 @@ private:
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);
static int nextCharCB(uint16_t conn_handle, const struct ble_gatt_error *error,
const struct ble_gatt_chr *chr, void *arg);
// Private properties
NimBLEUUID m_uuid;
uint8_t m_charProp;
uint16_t m_handle;
uint16_t m_defHandle;
uint16_t m_endHandle;
NimBLERemoteService* m_pRemoteService;
std::string m_value;
notify_callback m_notifyCallback;

View File

@@ -223,6 +223,20 @@ bool NimBLERemoteService::retrieveCharacteristics(const NimBLEUUID *uuid_filter)
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
if(taskData.rc == 0){
if (uuid_filter == nullptr) {
if (m_characteristicVector.size() > 1) {
for (auto it = m_characteristicVector.begin(); it != m_characteristicVector.end(); ++it ) {
auto nx = std::next(it, 1);
if (nx == m_characteristicVector.end()) {
break;
}
(*it)->m_endHandle = (*nx)->m_defHandle - 1;
}
}
m_characteristicVector.back()->m_endHandle = getEndHandle();
}
NIMBLE_LOGD(LOG_TAG, "<< retrieveCharacteristics()");
return true;
}
@@ -249,23 +263,6 @@ uint16_t NimBLERemoteService::getEndHandle() {
return m_endHandle;
} // getEndHandle
/**
* @brief Get the end handle of specified NimBLERemoteCharacteristic.
*/
uint16_t NimBLERemoteService::getEndHandle(NimBLERemoteCharacteristic *pCharacteristic) {
uint16_t endHandle = m_endHandle;
for(auto &it: m_characteristicVector) {
uint16_t defHandle = it->getDefHandle() - 1;
if(defHandle > pCharacteristic->getDefHandle() && endHandle > defHandle) {
endHandle = defHandle;
}
}
return endHandle;
} // getEndHandle
/**
* @brief Get the service start handle.

View File

@@ -70,7 +70,6 @@ private:
uint16_t getStartHandle();
uint16_t getEndHandle();
uint16_t getEndHandle(NimBLERemoteCharacteristic *pCharacteristic);
void releaseSemaphores();
// Properties

View File

@@ -110,7 +110,7 @@ NimBLEScan::~NimBLEScan() {
advertisedDevice->m_timestamp = time(nullptr);
advertisedDevice->setRSSI(event->disc.rssi);
advertisedDevice->setPayload(event->disc.data, event->disc.length_data,
advertisedDevice->setPayload(event->disc.data, event->disc.length_data,
event->disc.event_type == BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP);
if (pScan->m_pAdvertisedDeviceCallbacks) {
@@ -128,7 +128,7 @@ NimBLEScan::~NimBLEScan() {
advertisedDevice->m_callbackSent = true;
pScan->m_pAdvertisedDeviceCallbacks->onResult(advertisedDevice);
}
// If not storing results and we have invoked the callback, delete the device.
// If not storing results and we have invoked the callback, delete the device.
if(pScan->m_maxResults == 0 && advertisedDevice->m_callbackSent) {
pScan->erase(advertisedAddress);
}
@@ -316,6 +316,8 @@ bool NimBLEScan::start(uint32_t duration, void (*scanCompleteCB)(NimBLEScanResul
break;
case BLE_HS_EALREADY:
// Clear the cache if already scanning in case an advertiser was missed.
clearDuplicateCache();
break;
case BLE_HS_EBUSY:
@@ -398,6 +400,16 @@ bool NimBLEScan::stop() {
} // stop
/**
* @brief Clears the duplicate scan filter cache.
*/
void NimBLEScan::clearDuplicateCache() {
#ifdef CONFIG_IDF_TARGET_ESP32 // Not available for ESP32C3
esp_ble_scan_dupilcate_list_flush();
#endif
}
/**
* @brief Delete peer device from the scan results vector.
* @param [in] address The address of the device to delete from the results.
@@ -453,6 +465,7 @@ void NimBLEScan::clearResults() {
delete it;
}
m_scanResults.m_advertisedDevicesVector.clear();
clearDuplicateCache();
}

View File

@@ -70,6 +70,7 @@ public:
void setDuplicateFilter(bool enabled);
void setLimitedOnly(bool enabled);
void setFilterPolicy(uint8_t filter);
void clearDuplicateCache();
bool stop();
void clearResults();
NimBLEScanResults getResults();

View File

@@ -37,6 +37,7 @@ static NimBLEServerCallbacks defaultCallbacks;
* the NimBLEDevice class.
*/
NimBLEServer::NimBLEServer() {
memset(m_indWait, BLE_HS_CONN_HANDLE_NONE, sizeof(m_indWait));
// m_svcChgChrHdl = 0xffff; // Future Use
m_pServerCallbacks = &defaultCallbacks;
m_gattsStarted = false;
@@ -90,12 +91,7 @@ NimBLEService* NimBLEServer::createService(const NimBLEUUID &uuid, uint32_t numH
NimBLEService* pService = new NimBLEService(uuid, numHandles, this);
m_svcVec.push_back(pService); // Save a reference to this service being on this server.
if(m_gattsStarted) {
ble_svc_gatt_changed(0x0001, 0xffff);
m_svcChanged = true;
resetGATT();
}
serviceChanged();
NIMBLE_LOGD(LOG_TAG, "<< createService");
return pService;
@@ -156,6 +152,18 @@ NimBLEAdvertising* NimBLEServer::getAdvertising() {
} // getAdvertising
/**
* @brief Sends a service changed notification and resets the GATT server.
*/
void NimBLEServer::serviceChanged() {
if(m_gattsStarted) {
m_svcChanged = true;
ble_svc_gatt_changed(0x0001, 0xffff);
resetGATT();
}
}
/**
* @brief Start the GATT server. Required to be called after setup of all
* services and characteristics / descriptors for the NimBLE host to register them.
@@ -253,6 +261,63 @@ size_t NimBLEServer::getConnectedCount() {
} // getConnectedCount
/**
* @brief Get the vector of the connected client ID's.
*/
std::vector<uint16_t> NimBLEServer::getPeerDevices() {
return m_connectedPeersVec;
} // getPeerDevices
/**
* @brief Get the connection information of a connected peer by vector index.
* @param [in] index The vector index of the peer.
*/
NimBLEConnInfo NimBLEServer::getPeerInfo(size_t index) {
if (index >= m_connectedPeersVec.size()) {
NIMBLE_LOGE(LOG_TAG, "No peer at index %u", index);
return NimBLEConnInfo();
}
return getPeerIDInfo(m_connectedPeersVec[index]);
} // getPeerInfo
/**
* @brief Get the connection information of a connected peer by address.
* @param [in] address The address of the peer.
*/
NimBLEConnInfo NimBLEServer::getPeerInfo(const NimBLEAddress& address) {
ble_addr_t peerAddr;
memcpy(&peerAddr.val, address.getNative(),6);
peerAddr.type = address.getType();
NimBLEConnInfo peerInfo;
int rc = ble_gap_conn_find_by_addr(&peerAddr, &peerInfo.m_desc);
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "Peer info not found");
}
return peerInfo;
} // getPeerInfo
/**
* @brief Get the connection information of a connected peer by connection ID.
* @param [in] id The connection id of the peer.
*/
NimBLEConnInfo NimBLEServer::getPeerIDInfo(uint16_t id) {
NimBLEConnInfo peerInfo;
int rc = ble_gap_conn_find(id, &peerInfo.m_desc);
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "Peer info not found");
}
return peerInfo;
} // getPeerIDInfo
/**
* @brief Handle a GATT Server Event.
*
@@ -280,7 +345,9 @@ size_t NimBLEServer::getConnectedCount() {
server->m_connectedPeersVec.push_back(event->connect.conn_handle);
rc = ble_gap_conn_find(event->connect.conn_handle, &desc);
assert(rc == 0);
if (rc != 0) {
return 0;
}
server->m_pServerCallbacks->onConnect(server);
server->m_pServerCallbacks->onConnect(server, &desc);
@@ -335,7 +402,9 @@ size_t NimBLEServer::getConnectedCount() {
(it->getProperties() & BLE_GATT_CHR_F_READ_ENC))
{
rc = ble_gap_conn_find(event->subscribe.conn_handle, &desc);
assert(rc == 0);
if (rc != 0) {
break;
}
if(!desc.sec_state.encrypted) {
NimBLEDevice::startSecurity(event->subscribe.conn_handle);
@@ -354,22 +423,54 @@ size_t NimBLEServer::getConnectedCount() {
NIMBLE_LOGI(LOG_TAG, "mtu update event; conn_handle=%d mtu=%d",
event->mtu.conn_handle,
event->mtu.value);
rc = ble_gap_conn_find(event->mtu.conn_handle, &desc);
if (rc != 0) {
return 0;
}
server->m_pServerCallbacks->onMTUChange(event->mtu.value, &desc);
return 0;
} // BLE_GAP_EVENT_MTU
case BLE_GAP_EVENT_NOTIFY_TX: {
if(event->notify_tx.indication && event->notify_tx.status != 0) {
for(auto &it : server->m_notifyChrVec) {
if(it->getHandle() == event->notify_tx.attr_handle) {
if(it->m_pTaskData != nullptr) {
it->m_pTaskData->rc = event->notify_tx.status;
xTaskNotifyGive(it->m_pTaskData->task);
}
break;
}
NimBLECharacteristic *pChar = nullptr;
for(auto &it : server->m_notifyChrVec) {
if(it->getHandle() == event->notify_tx.attr_handle) {
pChar = it;
}
}
if(pChar == nullptr) {
return 0;
}
NimBLECharacteristicCallbacks::Status statusRC;
if(event->notify_tx.indication) {
if(event->notify_tx.status != 0) {
if(event->notify_tx.status == BLE_HS_EDONE) {
statusRC = NimBLECharacteristicCallbacks::Status::SUCCESS_INDICATE;
} else if(rc == BLE_HS_ETIMEOUT) {
statusRC = NimBLECharacteristicCallbacks::Status::ERROR_INDICATE_TIMEOUT;
} else {
statusRC = NimBLECharacteristicCallbacks::Status::ERROR_INDICATE_FAILURE;
}
} else {
return 0;
}
server->clearIndicateWait(event->notify_tx.conn_handle);
} else {
if(event->notify_tx.status == 0) {
statusRC = NimBLECharacteristicCallbacks::Status::SUCCESS_NOTIFY;
} else {
statusRC = NimBLECharacteristicCallbacks::Status::ERROR_GATT;
}
}
pChar->m_pCallbacks->onStatus(pChar, statusRC, event->notify_tx.status);
return 0;
} // BLE_GAP_EVENT_NOTIFY_TX
@@ -392,7 +493,10 @@ size_t NimBLEServer::getConnectedCount() {
/* Delete the old bond. */
rc = ble_gap_conn_find(event->repeat_pairing.conn_handle, &desc);
assert(rc == 0);
if (rc != 0){
return BLE_GAP_REPEAT_PAIRING_IGNORE;
}
ble_store_util_delete_peer(&desc.peer_id_addr);
/* Return BLE_GAP_REPEAT_PAIRING_RETRY to indicate that the host should
@@ -533,7 +637,7 @@ void NimBLEServer::removeService(NimBLEService* service, bool deleteSvc) {
if(service->m_removed > 0) {
if(deleteSvc) {
for(auto it = m_svcVec.begin(); it != m_svcVec.end(); ++it) {
if ((*it)->getUUID() == service->getUUID()) {
if ((*it) == service) {
delete *it;
m_svcVec.erase(it);
break;
@@ -549,32 +653,35 @@ void NimBLEServer::removeService(NimBLEService* service, bool deleteSvc) {
return;
}
service->m_removed = deleteSvc ? 2 : 1;
m_svcChanged = true;
ble_svc_gatt_changed(0x0001, 0xffff);
resetGATT();
service->m_removed = deleteSvc ? NIMBLE_ATT_REMOVE_DELETE : NIMBLE_ATT_REMOVE_HIDE;
serviceChanged();
NimBLEDevice::getAdvertising()->removeServiceUUID(service->getUUID());
}
/**
* @brief Adds a service which was already created, but removed from availability.
* @brief Adds a service which was either already created but removed from availability,\n
* or created and later added to services list.
* @param [in] service The service object to add.
* @note If it is desired to advertise the service it must be added by
* calling NimBLEAdvertising::addServiceUUID.
*/
void NimBLEServer::addService(NimBLEService* service) {
// If adding a service that was not removed just return.
// Check that a service with the supplied UUID does not already exist.
if(getServiceByUUID(service->getUUID()) != nullptr) {
NIMBLE_LOGW(LOG_TAG, "Warning creating a duplicate service UUID: %s",
std::string(service->getUUID()).c_str());
}
// If adding a service that was not removed add it and return.
// Else reset GATT and send service changed notification.
if(service->m_removed == 0) {
m_svcVec.push_back(service);
return;
}
service->m_removed = 0;
m_svcChanged = true;
ble_svc_gatt_changed(0x0001, 0xffff);
resetGATT();
serviceChanged();
}
@@ -593,7 +700,7 @@ void NimBLEServer::resetGATT() {
for(auto it = m_svcVec.begin(); it != m_svcVec.end(); ) {
if ((*it)->m_removed > 0) {
if ((*it)->m_removed == 2) {
if ((*it)->m_removed == NIMBLE_ATT_REMOVE_DELETE) {
delete *it;
it = m_svcVec.erase(it);
} else {
@@ -668,6 +775,27 @@ void NimBLEServer::updateConnParams(uint16_t conn_handle,
}// updateConnParams
bool NimBLEServer::setIndicateWait(uint16_t conn_handle) {
for(auto i = 0; i < CONFIG_BT_NIMBLE_MAX_CONNECTIONS; i++) {
if(m_indWait[i] == conn_handle) {
return false;
}
}
return true;
}
void NimBLEServer::clearIndicateWait(uint16_t conn_handle) {
for(auto i = 0; i < CONFIG_BT_NIMBLE_MAX_CONNECTIONS; i++) {
if(m_indWait[i] == conn_handle) {
m_indWait[i] = BLE_HS_CONN_HANDLE_NONE;
return;
}
}
}
/** Default callback handlers */
void NimBLEServerCallbacks::onConnect(NimBLEServer* pServer) {
@@ -688,6 +816,10 @@ void NimBLEServerCallbacks::onDisconnect(NimBLEServer* pServer, ble_gap_conn_des
NIMBLE_LOGD("NimBLEServerCallbacks", "onDisconnect(): Default");
} // onDisconnect
void NimBLEServerCallbacks::onMTUChange(uint16_t MTU, ble_gap_conn_desc* desc) {
NIMBLE_LOGD("NimBLEServerCallbacks", "onMTUChange(): Default");
} // onMTUChange
uint32_t NimBLEServerCallbacks::onPassKeyRequest(){
NIMBLE_LOGD("NimBLEServerCallbacks", "onPassKeyRequest: default: 123456");
return 123456;

View File

@@ -20,11 +20,15 @@
#include "nimconfig.h"
#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#define NIMBLE_ATT_REMOVE_HIDE 1
#define NIMBLE_ATT_REMOVE_DELETE 2
#include "NimBLEUtils.h"
#include "NimBLEAddress.h"
#include "NimBLEAdvertising.h"
#include "NimBLEService.h"
#include "NimBLESecurity.h"
#include "NimBLEConnInfo.h"
class NimBLEService;
@@ -58,13 +62,17 @@ public:
uint16_t minInterval, uint16_t maxInterval,
uint16_t latency, uint16_t timeout);
uint16_t getPeerMTU(uint16_t conn_id);
// std::vector<uint16_t> getPeerDevices();
std::vector<uint16_t> getPeerDevices();
NimBLEConnInfo getPeerInfo(size_t index);
NimBLEConnInfo getPeerInfo(const NimBLEAddress& address);
NimBLEConnInfo getPeerIDInfo(uint16_t id);
void advertiseOnDisconnect(bool);
private:
NimBLEServer();
~NimBLEServer();
friend class NimBLECharacteristic;
friend class NimBLEService;
friend class NimBLEDevice;
friend class NimBLEAdvertising;
@@ -73,6 +81,7 @@ private:
bool m_svcChanged;
NimBLEServerCallbacks* m_pServerCallbacks;
bool m_deleteCallbacks;
uint16_t m_indWait[CONFIG_BT_NIMBLE_MAX_CONNECTIONS];
std::vector<uint16_t> m_connectedPeersVec;
// uint16_t m_svcChgChrHdl; // Future use
@@ -81,7 +90,10 @@ private:
std::vector<NimBLECharacteristic*> m_notifyChrVec;
static int handleGapEvent(struct ble_gap_event *event, void *arg);
void serviceChanged();
void resetGATT();
bool setIndicateWait(uint16_t conn_handle);
void clearIndicateWait(uint16_t conn_handle);
}; // NimBLEServer
@@ -124,6 +136,14 @@ public:
*/
virtual void onDisconnect(NimBLEServer* pServer, ble_gap_conn_desc* desc);
/**
* @brief Called when the connection MTU changes.
* @param [in] MTU The new MTU value.
* @param [in] desc A pointer to the connection description structure containig information
* about the connection.
*/
virtual void onMTUChange(uint16_t MTU, ble_gap_conn_desc* desc);
/**
* @brief Called when a client requests a passkey for pairing.
* @return The passkey to be sent to the client.

View File

@@ -35,7 +35,7 @@ static const char* LOG_TAG = "NimBLEService"; // Tag for logging.
* @brief Construct an instance of the NimBLEService
* @param [in] uuid The UUID of the service.
* @param [in] numHandles The maximum number of handles associated with the service.
* @param [in] a pointer to the server instance that this service belongs to.
* @param [in] pServer A pointer to the server instance that this service belongs to.
*/
NimBLEService::NimBLEService(const char* uuid, uint16_t numHandles, NimBLEServer* pServer)
: NimBLEService(NimBLEUUID(uuid), numHandles, pServer) {
@@ -46,7 +46,7 @@ NimBLEService::NimBLEService(const char* uuid, uint16_t numHandles, NimBLEServer
* @brief Construct an instance of the BLEService
* @param [in] uuid The UUID of the service.
* @param [in] numHandles The maximum number of handles associated with the service.
* @param [in] a pointer to the server instance that this service belongs to.
* @param [in] pServer A pointer to the server instance that this service belongs to.
*/
NimBLEService::NimBLEService(const NimBLEUUID &uuid, uint16_t numHandles, NimBLEServer* pServer) {
m_uuid = uuid;
@@ -118,7 +118,12 @@ NimBLEUUID NimBLEService::getUUID() {
*/
bool NimBLEService::start() {
NIMBLE_LOGD(LOG_TAG, ">> start(): Starting service: %s", toString().c_str());
int rc = 0;
// Rebuild the service definition if the server attributes have changed.
if(getServer()->m_svcChanged && m_pSvcDef != nullptr) {
delete(m_pSvcDef);
m_pSvcDef = nullptr;
}
if(m_pSvcDef == nullptr) {
// Nimble requires an array of services to be sent to the api
@@ -132,8 +137,23 @@ bool NimBLEService::start() {
svc[0].uuid = &m_uuid.getNative()->u;
svc[0].includes = NULL;
size_t numChrs = m_chrVec.size();
int removedCount = 0;
for(auto it = m_chrVec.begin(); it != m_chrVec.end(); ) {
if ((*it)->m_removed > 0) {
if ((*it)->m_removed == NIMBLE_ATT_REMOVE_DELETE) {
delete *it;
it = m_chrVec.erase(it);
} else {
++removedCount;
++it;
}
continue;
}
++it;
}
size_t numChrs = m_chrVec.size() - removedCount;
NIMBLE_LOGD(LOG_TAG,"Adding %d characteristics for service %s", numChrs, toString().c_str());
if(!numChrs){
@@ -142,40 +162,60 @@ bool NimBLEService::start() {
// Nimble requires the last characteristic to have it's uuid = 0 to indicate the end
// of the characteristics for the service. We create 1 extra and set it to null
// for this purpose.
pChr_a = new ble_gatt_chr_def[numChrs+1];
NimBLECharacteristic* pCharacteristic = *m_chrVec.begin();
pChr_a = new ble_gatt_chr_def[numChrs + 1];
uint8_t i = 0;
for(auto chr_it = m_chrVec.begin(); chr_it != m_chrVec.end(); ++chr_it) {
if((*chr_it)->m_removed > 0) {
continue;
}
for(uint8_t i=0; i < numChrs;) {
uint8_t numDscs = pCharacteristic->m_dscVec.size();
removedCount = 0;
for(auto it = (*chr_it)->m_dscVec.begin(); it != (*chr_it)->m_dscVec.end(); ) {
if ((*it)->m_removed > 0) {
if ((*it)->m_removed == NIMBLE_ATT_REMOVE_DELETE) {
delete *it;
it = (*chr_it)->m_dscVec.erase(it);
} else {
++removedCount;
++it;
}
continue;
}
++it;
}
size_t numDscs = (*chr_it)->m_dscVec.size() - removedCount;
if(!numDscs){
pChr_a[i].descriptors = NULL;
} else {
// Must have last descriptor uuid = 0 so we have to create 1 extra
pDsc_a = new ble_gatt_dsc_def[numDscs+1];
NimBLEDescriptor* pDescriptor = *pCharacteristic->m_dscVec.begin();
for(uint8_t d=0; d < numDscs;) {
pDsc_a[d].uuid = &pDescriptor->m_uuid.getNative()->u;
pDsc_a[d].att_flags = pDescriptor->m_properties;
uint8_t d = 0;
for(auto dsc_it = (*chr_it)->m_dscVec.begin(); dsc_it != (*chr_it)->m_dscVec.end(); ++dsc_it ) {
if((*dsc_it)->m_removed > 0) {
continue;
}
pDsc_a[d].uuid = &(*dsc_it)->m_uuid.getNative()->u;
pDsc_a[d].att_flags = (*dsc_it)->m_properties;
pDsc_a[d].min_key_size = 0;
pDsc_a[d].access_cb = NimBLEDescriptor::handleGapEvent;
pDsc_a[d].arg = pDescriptor;
d++;
pDescriptor = *(pCharacteristic->m_dscVec.begin() + d);
pDsc_a[d].arg = (*dsc_it);
++d;
}
pDsc_a[numDscs].uuid = NULL;
pChr_a[i].descriptors = pDsc_a;
}
pChr_a[i].uuid = &pCharacteristic->m_uuid.getNative()->u;
pChr_a[i].uuid = &(*chr_it)->m_uuid.getNative()->u;
pChr_a[i].access_cb = NimBLECharacteristic::handleGapEvent;
pChr_a[i].arg = pCharacteristic;
pChr_a[i].flags = pCharacteristic->m_properties;
pChr_a[i].arg = (*chr_it);
pChr_a[i].flags = (*chr_it)->m_properties;
pChr_a[i].min_key_size = 0;
pChr_a[i].val_handle = &pCharacteristic->m_handle;
i++;
pCharacteristic = *(m_chrVec.begin() + i);
pChr_a[i].val_handle = &(*chr_it)->m_handle;
++i;
}
pChr_a[numChrs].uuid = NULL;
@@ -187,7 +227,7 @@ bool NimBLEService::start() {
m_pSvcDef = svc;
}
rc = ble_gatts_count_cfg((const ble_gatt_svc_def*)m_pSvcDef);
int rc = ble_gatts_count_cfg((const ble_gatt_svc_def*)m_pSvcDef);
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "ble_gatts_count_cfg failed, rc= %d, %s", rc, NimBLEUtils::returnCodeToString(rc));
return false;
@@ -239,13 +279,64 @@ NimBLECharacteristic* NimBLEService::createCharacteristic(const NimBLEUUID &uuid
std::string(uuid).c_str());
}
// Remember this characteristic in our vector of characteristics.
m_chrVec.push_back(pCharacteristic);
addCharacteristic(pCharacteristic);
return pCharacteristic;
} // createCharacteristic
/**
* @brief Add a characteristic to the service.
* @param[in] pCharacteristic A pointer to the characteristic instance to add to the service.
*/
void NimBLEService::addCharacteristic(NimBLECharacteristic* pCharacteristic) {
bool foundRemoved = false;
if(pCharacteristic->m_removed > 0) {
for(auto& it : m_chrVec) {
if(it == pCharacteristic) {
foundRemoved = true;
pCharacteristic->m_removed = 0;
}
}
}
if(!foundRemoved) {
m_chrVec.push_back(pCharacteristic);
}
pCharacteristic->setService(this);
getServer()->serviceChanged();
} // addCharacteristic
/**
* @brief Remove a characteristic from the service.
* @param[in] pCharacteristic A pointer to the characteristic instance to remove from the service.
* @param[in] deleteChr If true it will delete the characteristic instance and free it's resources.
*/
void NimBLEService::removeCharacteristic(NimBLECharacteristic* pCharacteristic, bool deleteChr) {
// Check if the characteristic was already removed and if so, check if this
// is being called to delete the object and do so if requested.
// Otherwise, ignore the call and return.
if(pCharacteristic->m_removed > 0) {
if(deleteChr) {
for(auto it = m_chrVec.begin(); it != m_chrVec.end(); ++it) {
if ((*it) == pCharacteristic) {
m_chrVec.erase(it);
delete *it;
break;
}
}
}
return;
}
pCharacteristic->m_removed = deleteChr ? NIMBLE_ATT_REMOVE_DELETE : NIMBLE_ATT_REMOVE_HIDE;
getServer()->serviceChanged();
} // removeCharacteristic
/**
* @brief Get a pointer to the characteristic object with the specified UUID.
* @param [in] uuid The UUID of the characteristic.

View File

@@ -36,6 +36,10 @@ class NimBLECharacteristic;
class NimBLEService {
public:
NimBLEService(const char* uuid, uint16_t numHandles, NimBLEServer* pServer);
NimBLEService(const NimBLEUUID &uuid, uint16_t numHandles, NimBLEServer* pServer);
~NimBLEService();
NimBLEServer* getServer();
NimBLEUUID getUUID();
@@ -55,6 +59,8 @@ public:
NIMBLE_PROPERTY::READ |
NIMBLE_PROPERTY::WRITE);
void addCharacteristic(NimBLECharacteristic* pCharacteristic);
void removeCharacteristic(NimBLECharacteristic* pCharacteristic, bool deleteChr = false);
NimBLECharacteristic* getCharacteristic(const char* uuid, uint16_t instanceId = 0);
NimBLECharacteristic* getCharacteristic(const NimBLEUUID &uuid, uint16_t instanceId = 0);
NimBLECharacteristic* getCharacteristicByHandle(uint16_t handle);
@@ -65,9 +71,6 @@ public:
private:
NimBLEService(const char* uuid, uint16_t numHandles, NimBLEServer* pServer);
NimBLEService(const NimBLEUUID &uuid, uint16_t numHandles, NimBLEServer* pServer);
~NimBLEService();
friend class NimBLEServer;
friend class NimBLEDevice;

View File

@@ -277,10 +277,6 @@ std::string NimBLEUUID::toString() const {
*/
bool NimBLEUUID::operator ==(const NimBLEUUID & rhs) const {
if(m_valueSet && rhs.m_valueSet) {
NIMBLE_LOGD(LOG_TAG,"Comparing UUIDs; type %u to %u; UUID %s to %s",
m_uuid.u.type, rhs.m_uuid.u.type,
this->toString().c_str(), rhs.toString().c_str());
if(m_uuid.u.type != rhs.m_uuid.u.type) {
uint8_t uuidBase[16] = {
0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80,

View File

@@ -41,13 +41,21 @@
#endif
#if defined(CONFIG_SCAN_DUPLICATE_TYPE) && !defined(CONFIG_BTDM_SCAN_DUPL_TYPE)
#define CONFIG_BTDM_SCAN_DUPL_TYPE CONFIG_SCAN_DUPLICATE_TYPE
#define CONFIG_BTDM_SCAN_DUPL_TYPE CONFIG_SCAN_DUPLICATE_TYPE
#endif
#if defined(CONFIG_BT_CTRL_SCAN_DUPL_TYPE) && !defined(CONFIG_BTDM_SCAN_DUPL_TYPE)
#define CONFIG_BTDM_SCAN_DUPL_TYPE CONFIG_BT_CTRL_SCAN_DUPL_TYPE
#endif
#if defined(CONFIG_DUPLICATE_SCAN_CACHE_SIZE) && !defined(CONFIG_BTDM_SCAN_DUPL_CACHE_SIZE)
#define CONFIG_BTDM_SCAN_DUPL_CACHE_SIZE CONFIG_DUPLICATE_SCAN_CACHE_SIZE
#define CONFIG_BTDM_SCAN_DUPL_CACHE_SIZE CONFIG_DUPLICATE_SCAN_CACHE_SIZE
#endif
#if defined(CONFIG_BT_CTRL_SCAN_DUPL_CACHE_SIZE) && !defined(CONFIG_BTDM_SCAN_DUPL_CACHE_SIZE)
#define CONFIG_BTDM_SCAN_DUPL_CACHE_SIZE CONFIG_BT_CTRL_SCAN_DUPL_CACHE_SIZE
#endif
#if defined(CONFIG_NIMBLE_MAX_CONNECTIONS ) && !defined(CONFIG_BT_NIMBLE_MAX_CONNECTIONS)
#define CONFIG_BT_NIMBLE_MAX_CONNECTIONS CONFIG_NIMBLE_MAX_CONNECTIONS
#define CONFIG_BT_NIMBLE_MAX_CONNECTIONS CONFIG_NIMBLE_MAX_CONNECTIONS
#endif