Compare commits

...

12 Commits

Author SHA1 Message Date
7b40829e77 Release 1.3.1 2021-08-04 18:55:34 -06:00
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
9961c5605c Update docs version. 2021-08-02 21:25:17 -06:00
d4e4074f5a Release 1.3.0 2021-08-02 21:14:12 -06:00
e5edc9d59e Add missing return documentation in NimBLEAdvertising. 2021-08-02 19:51:04 -06:00
2e1d78ff78 Code cleanup 2021-08-02 13:29:19 -06:00
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
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
7d01fa595d Fix compilation for ESP32C3. (#53) 2021-07-19 21:46:30 -06:00
2decc0682a Remove FreeRTOS from CmakeLists. 2021-07-14 10:30:41 -06:00
6ff1a49dd5 Update documentation. 2021-07-12 13:33:56 -06:00
09adf86036 Remove FreeRTOS files. 2021-07-12 10:25:32 -06:00
21 changed files with 289 additions and 476 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

@ -28,7 +28,6 @@ idf_component_register(
INCLUDE_DIRS
"src"
SRCS
"src/FreeRTOS.cpp"
"src/NimBLE2904.cpp"
"src/NimBLEAddress.cpp"
"src/NimBLEAdvertisedDevice.cpp"

View File

@ -5,7 +5,6 @@ cmake_minimum_required(VERSION 3.5)
set(SUPPORTED_TARGETS esp32)
set(COMPONENT_SRCS
"src/FreeRTOS.cpp"
"src/NimBLE2904.cpp"
"src/NimBLEAddress.cpp"
"src/NimBLEAdvertisedDevice.cpp"

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

@ -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",

View File

@ -45,14 +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_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,21 +96,63 @@ NimBLEDescriptor* NimBLECharacteristic::createDescriptor(const NimBLEUUID &uuid,
}
addDescriptor(pDescriptor);
return pDescriptor;
} // createDescriptor
/**
* @brief Add a descriptor to the characteristic.
* @param [in] A pointer to the descriptor to add.
* @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);
m_dscVec.push_back(pDescriptor);
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
/**
* @brief Return the BLE Descriptor for the given UUID.
* @param [in] uuid The UUID of the descriptor.

View File

@ -99,6 +99,7 @@ public:
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();
@ -152,6 +153,7 @@ private:
std::vector<NimBLEDescriptor*> m_dscVec;
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

@ -53,6 +53,7 @@ NimBLEDescriptor::NimBLEDescriptor(NimBLEUUID uuid, uint16_t properties, uint16_
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;

View File

@ -93,6 +93,7 @@ private:
uint8_t m_properties;
attr_value_t m_value;
portMUX_TYPE m_valMux;
uint8_t m_removed;
}; // NimBLEDescriptor

View File

@ -757,8 +757,12 @@ NimBLEAddress NimBLEDevice::getWhiteListAddress(size_t index) {
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;
@ -977,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

@ -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);
}
@ -404,7 +404,9 @@ bool NimBLEScan::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
}

View File

@ -91,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;
@ -157,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.
@ -416,6 +423,12 @@ NimBLEConnInfo NimBLEServer::getPeerIDInfo(uint16_t id) {
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
@ -624,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;
@ -640,11 +653,8 @@ 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());
}
@ -671,10 +681,7 @@ void NimBLEServer::addService(NimBLEService* service) {
}
service->m_removed = 0;
m_svcChanged = true;
ble_svc_gatt_changed(0x0001, 0xffff);
resetGATT();
serviceChanged();
}
@ -693,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 {
@ -809,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,6 +20,9 @@
#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"
@ -69,6 +72,7 @@ private:
NimBLEServer();
~NimBLEServer();
friend class NimBLECharacteristic;
friend class NimBLEService;
friend class NimBLEDevice;
friend class NimBLEAdvertising;
@ -86,6 +90,7 @@ 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);
@ -131,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,17 +279,63 @@ 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);
m_chrVec.push_back(pCharacteristic);
}
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.

View File

@ -60,6 +60,7 @@ public:
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);

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