update NimBLE-Arduino to 1.0.2

This commit is contained in:
Staars 2020-09-15 10:45:24 +02:00
parent bf115647c3
commit 3368b73753
57 changed files with 3005 additions and 1563 deletions

View File

@ -1,245 +0,0 @@
# Server API differnces:
### Characteristics:
When creating a characteristic the properties are now set with `NIMBLE_PROPERTY::XXXX` instead of `BLECharacteristic::XXXX`.
#### Previous:
```
BLECharacteristic::PROPERTY_READ |
BLECharacteristic::PROPERTY_WRITE
```
#### Changed to:
```
NIMBLE_PROPERTY::READ |
NIMBLE_PROPERTY::WRITE
```
#### The full list of properties:
```
NIMBLE_PROPERTY::READ
NIMBLE_PROPERTY::READ_ENC
NIMBLE_PROPERTY::READ_AUTHEN
NIMBLE_PROPERTY::READ_AUTHOR
NIMBLE_PROPERTY::WRITE
NIMBLE_PROPERTY::WRITE_NR
NIMBLE_PROPERTY::WRITE_ENC
NIMBLE_PROPERTY::WRITE_AUTHEN
NIMBLE_PROPERTY::WRITE_AUTHOR
NIMBLE_PROPERTY::BROADCAST
NIMBLE_PROPERTY::NOTIFY
NIMBLE_PROPERTY::INDICATE
```
### Descriptors:
Descriptors are now created using the NimBLEcharacteristic method `createDescriptor()`.
The previous method `addDescriptor()` is now a private function in the library.
This was done because the NimBLE host automatically creates a 0x2902 descriptor if a characteristic has notify or indicate properties applied.
Due to this fact, this library also creates one automatically for your application.
The only reason to manually create this descriptor now is to assign callback functions.
If you do not require this functionality you can safely exclude the manual creation of that descriptor.
For any other descriptor, (except 0x2904, see below) it should now be created just as characteristics are
by invoking the `NimBLECharacteristic::createDescriptor` methods.
Which are defined as:
```
NimBLEDescriptor* createDescriptor(const char* uuid,
uint32_t properties = NIMBLE_PROPERTY::READ |
NIMBLE_PROPERTY::WRITE,
uint16_t max_len = 100);
NimBLEDescriptor* createDescriptor(NimBLEUUID uuid,
uint32_t properties = NIMBLE_PROPERTY::READ |
NIMBLE_PROPERTY::WRITE,
uint16_t max_len = 100);
```
##### Example:
```
pDescriptor = pCharacteristic->createDescriptor("ABCD",
NIMBLE_PROPERTY::READ |
NIMBLE_PROPERTY::WRITE |
NIMBLE_PROPERTY::WRITE_ENC,
25);`
```
Would create a descriptor with the UUID 0xABCD, publicly readable but only writable if paired/bonded (encrypted) and has a max value length of 25 bytes.
For the 0x2904 descriptor, there is a special class that is created when you call `createDescriptor("2904")`.
The pointer returned is of the base class `NimBLEDescriptor` but the call will create the derived class of `NimBLE2904` so you must cast the returned pointer to `NimBLE2904*` to access the specific class methods.
##### Example:
```
p2904 = (NimBLE2904*)pCharacteristic->createDescriptor("2904");
```
#### Server Security:
Security is set on the characteristic or descriptor properties by applying one of the following:
```
NIMBLE_PROPERTY::READ_ENC
NIMBLE_PROPERTY::READ_AUTHEN
NIMBLE_PROPERTY::READ_AUTHOR
NIMBLE_PROPERTY::WRITE_ENC
NIMBLE_PROPERTY::WRITE_AUTHEN
NIMBLE_PROPERTY::WRITE_AUTHOR
```
When a peer wants to read or write a characteristic or descriptor with any of these properties applied
it will trigger the pairing process. By default the "just-works" pairing will be performed automatically.
This can be changed to use passkey authentication or numeric confirmation. See below for details.
# Client API Differences:
The `BLEAdvertisedDeviceCallbacks` class `onResult()` method now receives a pointer to the
`NimBLEAdvertisedDevice` object instead of a copy.
`NimBLEClient::connect()` now takes an extra parameter to indicate if the client should download the services
database from the peripheral, default value is true.
Defined as:
```
bool connect(NimBLEAdvertisedDevice* device, bool refreshServices = true);
bool connect(NimBLEAddress address, uint8_t type = BLE_ADDR_PUBLIC, bool refreshServices = true);
```
If set to false the client will use the services database it retrieved from the peripheral last time it connected.
This allows for faster connections and power saving if the devices just dropped connection and want to reconnect.
```
NimBLERemoteCharacteristic::writeValue();
NimBLERemoteCharacteristic::registerForNotify();
```
Now return true or false to indicate success or failure so you can choose to disconnect or try again.
```
NimBLEClient::getServices()
NimBLERemoteService::getCharacteristics()
```
Now return a pointer to a `std::vector` of the respective object database instead of `std::map`.
`NimBLERemoteService::getCharacteristicsByHandle()`
Has been removed from the API as it is no longer maintained in the library.
The last two above changes reduce the heap usage significantly with minimal application code adjustments.
**UPDATED** on June 21, 2020
> ```
> NimBLEClient::getServices(bool refresh = false)
> NimBLERemoteService::getCharacteristics(bool refresh = false)
> NimBLERemoteCharacteristic::getDecriptors(bool refresh = false)
>```
These methods now take an optional (bool) parameter.
If true it will clear the respective vector and retrieve all the respective attributes from the peripheral.
If false(default) it will return the respective vector empty or otherwise with the currently stored attributes.
**Removed:** the automatic discovery of all peripheral attributes as they consumed time and resources for data
the user may not be interested in.
**Added:** `NimBLEClient::discoverAtrributes()` for the user to discover all the peripheral attributes
to replace the the former functionality.
> ```
>getService(NimBLEUUID)
>getCharacteristic(NimBLEUUID)
>getDescriptor(NimBLEUUID)
>```
These methods will now check the respective vectors for the attribute object and, if not found, will retrieve (only)
the specified attribute from the peripheral.
These changes allow more control for the user to manage the resources used for the attributes.
***
#### Client Security:
The client will automatically initiate security when the peripheral responds that it's required.
The default configuration will use "just-works" pairing with no bonding, if you wish to enable bonding see below.
# Security:
Security callback functions are now incorporated in the client/server Callbacks class.
However backward compatibility with the `BLESecurity` class is retained to minimize app code changes.
The relevant server callbacks are defined as:
```
bool onConfirmPIN(uint32_t pin); // accept or reject the passkey
void onAuthenticationComplete(ble_gap_conn_desc* desc); // auth complete - details in desc
bool onPassKeyNotify(uint32_t pass_key); // receive the passkey sent by the client, accept or reject
```
The relevant client callbacks are defined as:
```
bool onConfirmPIN(uint32_t pin); // accept or reject the passkey
void onAuthenticationComplete(ble_gap_conn_desc* desc); // auth complete - details in desc
uint32_t onPassKeyRequest(); // return the passkey to send to the server
```
Security settings and IO capabilities are now set by the corresponding method of `NimBLEDevice::`.
```
static void setSecurityAuth(bool bonding, bool mitm, bool sc);
static void setSecurityAuth(uint8_t auth_req);
static void setSecurityIOCap(uint8_t iocap);
static void setSecurityInitKey(uint8_t init_key);
static void setSecurityRespKey(uint8_t init_key);
/**
* @brief Set the authorization mode for this device.
* @param bonding, if true we allow bonding, false no bonding will be performed.
* @param mitm, if true we are capable of man in the middle protection, false if not.
* @param sc, if true we will perform secure connection pairing, false we will use legacy pairing.
*/
void NimBLEDevice::setSecurityAuth(bool bonding, bool mitm, bool sc)
/**
* @brief Set the authorization mode for this device.
* @param A bitmap indicating what modes are supported.
* The bits are defined as follows:
** 0x01 BLE_SM_PAIR_AUTHREQ_BOND
** 0x04 BLE_SM_PAIR_AUTHREQ_MITM
** 0x08 BLE_SM_PAIR_AUTHREQ_SC
** 0x10 BLE_SM_PAIR_AUTHREQ_KEYPRESS - not yet supported.
** 0xe2 BLE_SM_PAIR_AUTHREQ_RESERVED - for reference only.
*/
void NimBLEDevice::setSecurityAuth(uint8_t auth_req)
/**
* @brief Set the Input/Output capabilities of this device.
* @param One of the following:
** 0x00 BLE_HS_IO_DISPLAY_ONLY DisplayOnly IO capability
** 0x01 BLE_HS_IO_DISPLAY_YESNO DisplayYesNo IO capability
** 0x02 BLE_HS_IO_KEYBOARD_ONLY KeyboardOnly IO capability
** 0x03 BLE_HS_IO_NO_INPUT_OUTPUT NoInputNoOutput IO capability
** 0x04 BLE_HS_IO_KEYBOARD_DISPLAY KeyboardDisplay Only IO capability
*/
void NimBLEDevice::setSecurityIOCap(uint8_t iocap)
/**
* @brief If we are the initiator of the security procedure this sets the keys we will distribute.
* @param A bitmap indicating which keys to distribute during pairing.
* The bits are defined as follows:
** 0x01: BLE_SM_PAIR_KEY_DIST_ENC - Distribute the encryption key.
** 0x02: BLE_SM_PAIR_KEY_DIST_ID - Distribute the ID key (IRK).
** 0x04: BLE_SM_PAIR_KEY_DIST_SIGN
** 0x08: BLE_SM_PAIR_KEY_DIST_LINK
*/
void NimBLEDevice::setSecurityInitKey(uint8_t init_key)
/**
* @brief Set the keys we are willing to accept during pairing.
* @param A bitmap indicating which keys to accept during pairing.
* The bits are defined as follows:
** 0x01: BLE_SM_PAIR_KEY_DIST_ENC - Accept the encryption key.
** 0x02: BLE_SM_PAIR_KEY_DIST_ID - Accept the ID key (IRK).
** 0x04: BLE_SM_PAIR_KEY_DIST_SIGN
** 0x08: BLE_SM_PAIR_KEY_DIST_LINK
*/
void NimBLEDevice::setSecurityRespKey(uint8_t init_key)
```
I'm sure there are more things I have forgotten but this is all the majors.
I will update this document as necessary.

View File

@ -0,0 +1,38 @@
# Changelog
All notable changes to this project will be documented in this file.
## [1.0.2] - 2020-09-13
### Changed
- `NimBLEAdvertising::start` Now takes 2 optional parameters, the first is the duration to advertise for (in seconds), the second is a
callback that is invoked when advertsing ends and takes a pointer to a `NimBLEAdvertising` object (similar to the `NimBLEScan::start` API).
- (Arduino) Maximum BLE connections can now be altered by only changing the value of `CONFIG_BT_NIMBLE_MAX_CONNECTIONS` in `nimconfig.h`.
Any changes to the controller max connection settings in `sdkconfig.h` will now have no effect when using this library.
- (Arduino) Revert the previous change to fix the advertising start delay. Instead a replacement fix that routes all BLE controller commands from
a task running on core 0 (same as the controller) has been implemented. This improves response times and reliability for all BLE functions.
## [1.0.1] - 2020-09-02
### Added
- Empty `NimBLEAddress` constructor: `NimBLEAddress()` produces an address of 00:00:00:00:00:00 type 0.
- Documentation of the difference of NimBLEAddress::getNative vs the original bluedroid library.
### Changed
- notify_callback typedef is now defined as std::function to enable the use of std::bind to call a class member function.
### Fixed
- Fix advertising start delay when first called.
## [1.0.0] - 2020-08-22
First stable release.
All the original library functionality is complete and many extras added with full documentation.

View File

@ -1,36 +1,19 @@
# *** UPDATES ***
**Breaking changes:**
**NEW** on June 21, 2020
> ```
> NimBLEClient::getServices(bool refresh = false)
> NimBLERemoteService::getCharacteristics(bool refresh = false)
> NimBLERemoteCharacteristic::getDecriptors(bool refresh = false)
>```
These methods now take an optional (bool) parameter.
If true it will clear the respective vector and retrieve all the respective attributes from the peripheral.
If false(default) it will return the respective vector empty or otherwise with the currently stored attributes.
**NEW** on May 23, 2020
Client and scan now use `std::vector` instead of `std::map` for storing the remote attribute database.
This change will affect your application code if you use `NimBLEClient::getServices()` or `NimBLERemoteService::getCharacteristics()`
in your application as they now return a pointer to `std::vector` of the respective attributes.
In addition `NimBLERemoteService::getCharacteristicsByHandle()` has been removed as it is no longer maintained in the library.
These changes were necessary due to the amount of resources required to use `std::map`, it was not justifed by any benfit it provided.
It is expected that there will be minimal impact on most applications, if you need help adjusting your code please create an issue.
[Latest release ![Release Version](https://img.shields.io/github/release/h2zero/NimBLE-Arduino.svg?style=plastic)
![Release Date](https://img.shields.io/github/release-date/h2zero/NimBLE-Arduino.svg?style=plastic)](https://github.com/h2zero/NimBLE-Arduino/releases/latest/)
<br/>
# NimBLE-Arduino
A fork of the NimBLE stack restructured for compilation in the Ardruino IDE with a CPP library for use with ESP32.
A fork of the NimBLE stack restructured for compilation in the Ardruino IDE with a CPP library for use with ESP32.
**Note for IDF users: This repo will not compile correctly in ESP-IDF. An ESP-IDF component version of this library can be [found here.](https://github.com/h2zero/esp-nimble-cpp)**
This library **significantly** reduces resource usage and improves performance for ESP32 BLE applications as compared
with the bluedroid based library. The goal is to maintain, as much as reasonable, compatibility with the original
library but refactored to use the NimBLE stack. In addition, this library will be more actively developed and maintained
to provide improved capabilites and stability over the original.
to provide improved capabilites and stability over the original.
<br/>
## Resource use improvement:
## Resource use improvement
### (Original) BLE_client example comparison (Debug):
#### Arduino BLE Library
@ -50,21 +33,29 @@ Memory after connection: Free Heap: **173300**
Sketch uses **603432** bytes (28%) of program storage space.
Memory after connection: Free Heap: **269792**
**As shown: there is nearly a 50% reduction in flash use and approx. 100kB less ram consumed!**
# Installation:
**As shown: there is nearly a 50% reduction in flash use and approx. 100kB less ram consumed!**
<br/>
Download as .zip and extract to Arduino/libraries folder, or in Arduino IDE from Sketch menu -> Include library -> Add .Zip library.
# Installation
**Arduino Library manager:** Go to `sketch` -> `Include Library` -> `Manage Libraries` and search for NimBLE and install.
**Alternatively:** Download as .zip and extract to Arduino/libraries folder, or in Arduino IDE from Sketch menu -> Include library -> Add .Zip library.
`#include "NimBLEDevice.h"` at the beginning of your sketch.
Tested and working with esp32-arduino v1.0.2 and 1.0.4 in Arduino IDE v1.8.12 and platform IO.
Tested and working with esp32-arduino in Arduino IDE and platform IO.
<br/>
# Using
This library is intended to be compatible with the original ESP32 BLE functions and types with minor changes.
# Usage:
If you have not used the original Bluedroid library please refer to the [New user guide](docs/New_user_guide.md).
This library is intended to be compatible with the original ESP32 BLE functions and types with minor changes.
If you are familiar with the original library, see: [The migration guide](docs/Migration_guide.md) for details about breaking changes and migration.
Also see [Improvements_and_updates](docs/Improvements_and_updates.md) for information about non-breaking changes.
[Full API documentation and class list can be found here.](https://h2zero.github.io/esp-nimble-cpp/)
Check the Refactored_original_examples in the examples folder for highlights of the differences with the original library.
@ -72,26 +63,25 @@ More advanced examples highlighting many available features are in examples/ Nim
Beacon examples provided by @beegee-tokyo are in examples/ BLE_Beacon_Scanner, BLE_EddystoneTLM_Beacon, BLE_EddystoneURL_Beacon.
Change the settings in the `nimconfig.h` file to customize NimBLE to your project, such as increasing max connections, default is 3.
Change the settings in the `src/nimconfig.h` file to customize NimBLE to your project,
such as increasing max connections, default is 3, absolute maximum connections is 9.
<br/>
# Development Status
This Library is tracking the esp-nimble repo, nimble-1.2.0-idf master branch, currently [@95bd864.](https://github.com/espressif/esp-nimble)
# Continuing development:
This Library is tracking the esp-nimble repo, nimble-1.2.0-idf master branch, currently [@46c1d9f.](https://github.com/espressif/esp-nimble)
Also tracking the NimBLE related changes in esp-idf, master branch, currently [@2ef4890.](https://github.com/espressif/esp-idf/tree/master/components/bt/host/nimble)
# Acknowledgments:
* @nkolban and @chegewara for the [original esp32 BLE library](https://github.com/nkolban/esp32-snippets) this project was derived from.
* @beegee-tokyo for contributing your time to test/debug and contributing the beacon examples.
* @Jeroen88 for the amazing help debugging and improving the client code.
# Todo:
1. Create documentation.
2. Add BLE Mesh code.
3. Expose more NimBLE features.
Also tracking the NimBLE related changes in ESP-IDF, master branch, currently [@2ef4890.](https://github.com/espressif/esp-idf/tree/master/components/bt/host/nimble)
<br/>
# Acknowledgments
* [nkolban](https://github.com/nkolban) and [chegewara](https://github.com/chegewara) for the [original esp32 BLE library](https://github.com/nkolban/esp32-snippets/tree/master/cpp_utils) this project was derived from.
* [beegee-tokyo](https://github.com/beegee-tokyo) for contributing your time to test/debug and contributing the beacon examples.
* [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

@ -0,0 +1,136 @@
# Improvements and updates
Many improvements have been made to this library vs the original, this is a brief overview of the most significant changes.
Refer to the [class documentation](https://h2zero.github.io/esp-nimble-cpp/annotated.html) for futher information on class specifics.
* [Server](#server)
* [Advertising](#advertising)
* [Client](#client)
* [General](#general)
<br/>
<a name="server"></a>
# Server
`NimBLECharacteristic::setValue(const T &s)`
`NimBLEDescriptor::setValue(const T &s)`
Now use a template to accomodate standard and custom types/values.
**Example**
```
struct my_struct{
uint8_t one;
uint16_t two;
uint32_t four;
uint64_t eight;
float flt;
}myStruct;
myStruct.one = 1;
myStruct.two = 2;
myStruct.four = 4;
myStruct.eight = 8;
myStruct.flt = 1234.56;
pCharacteristic->setValue(myStruct);
```
This will send the struct to the recieving client when read or a notification sent.
`NimBLECharacteristic::getValue` now takes an optional timestamp parameter which will update it's value with
the time the last value was recieved. In addition an overloaded template has been added to retrieve the value
as a type specified by the user.
**Example**
```
time_t timestamp;
myStruct = pCharacteristic->getValue<myStruct>(&timestamp); // timestamp optional
```
<br/>
**Advertising will automatically start when a client disconnects.**
A new method `NimBLEServer::advertiseOnDisconnect(bool)` has been implemented to control this, true(default) = enabled.
<br/>
`NimBLEServer::removeService` takes an additional parameter `bool deleteSvc` that if true will delete the service
and all characteristics / descriptors belonging to it and invalidating any pointers to them.
If false the service is only removed from visibility by clients. The pointers to the service and
it's characteristics / descriptors will remain valid and the service can be re-added in the future
using `NimBLEServer::addService`.
<br/>
<a name="advertising"></a>
# Advertising
`NimBLEAdvertising::start`
Now takes 2 optional parameters, the first is the duration to advertise for (in seconds), the second is a callback
that is invoked when advertsing ends and takes a pointer to a `NimBLEAdvertising` object (similar to the `NimBLEScan::start` API).
This provides an opportunity to update the advertisment data if desired.
<br/>
<a name="client"></a>
# Client
`NimBLERemoteCharacteristic::readValue(time_t\*, bool)`
`NimBLERemoteDescriptor::readValue(bool)`
Have been added as templates to allow reading the values as any specified type.
**Example**
```
struct my_struct{
uint8_t one;
uint16_t two;
uint32_t four;
uint64_t eight;
float flt;
}myStruct;
time_t timestamp;
myStruct = pRemoteCharacteristic->readValue<myStruct>(&timestamp); // timestamp optional
```
<br/>
`NimBLERemoteCharacteristic::registerForNotify`
Has been **deprecated** as now the internally stored characteristic value is updated when notification/indication is recieved.
`NimBLERemoteCharacteristic::subscribe` and `NimBLERemoteCharacteristic::unsubscribe` have been implemented to replace it.
A callback is no longer requred to get the most recent value unless timing is important. Instead, the application can call `NimBLERemoteCharacteristic::getValue` to
get the last updated value any time.
In addition `NimBLERemoteCharacteristic::readValue` and `NimBLERemoteCharacteristic::getValue` take an optional timestamp parameter which will update it's value with
the time the last value was recieved.
> NimBLEClient::getService
> NimBLERemoteService::getCharacteristic
> NimBLERemoteCharacteristic::getDescriptor
These methods will now check the respective vectors for the attribute object and, if not found, will retrieve (only)
the specified attribute from the peripheral.
These changes allow more control for the user to manage the resources used for the attributes.
<br/>
`NimBLEClient::connect()` can now be called without an address or advertised device parameter. This will connect to the
device with the address previously set when last connected or set with `NimBLEDevice::setPeerAddress()`.
<a name="general"></a>
# General
To reduce resource use all instances of `std::map` have been replaced with `std::vector`.
Use of `FreeRTOS::Semaphore` has been removed as it was consuming too much ram, the related files have been left in place to accomodate application use.
Operators `==`, `!=` and `std::string` have been added to `NimBLEAddress` and `NimBLEUUID` for easier comparison and logging.
New constructor for `NimBLEUUID(uint32_t, uint16_t, uint16_t, uint64_t)` added to lower memory use vs string construction. See: [#21](https://github.com/h2zero/NimBLE-Arduino/pull/21).
Security/pairing operations are now handled in the respective `NimBLEClientCallbacks` and `NimBLEServerCallbacks` classes, `NimBLESecurity`(deprecated) remains for backward compatibility.
Configuration options have been added to add or remove debugging information, when disabled (default) significatly reduces binary size.
In ESP-IDF the options are in menuconfig: `Main menu -> ESP-NimBLE-cpp configuration`.
For Arduino the options must be commented / uncommented in nimconfig.h.
<br/>

View File

@ -0,0 +1,398 @@
# Migrating from Bluedroid to NimBLE
This guide describes the required changes to existing projects migrating from the original bluedroid API to NimBLE.
**The changes listed here are only the required changes that must be made**, and a short overview of options for migrating existing applications.
For more information on the improvements and additions please refer to the [class documentation](https://h2zero.github.io/esp-nimble-cpp/annotated.html) and [Improvements and updates](Improvements_and_updates.md)
* [General Changes](#general-information)
* [Server](#server-api)
* [Services](#services)
* [characteristics](#characteristics)
* [descriptors](#descriptors)
* [Security](#server-security)
* [Advertising](#advertising-api)
* [Client](#client-api)
* [Remote Services](#remote-services)
* [Remote characteristics](#remote-characteristics)
* [Security](#client-security)
* [General Security](#security-api)
* [Configuration](#arduino-configuration)
<br/>
<a name="general-information"></a>
## General Information
### Header Files
All classes are accessible by including `NimBLEDevice.h` in your application, no further headers need to be included.
(Mainly for Arduino) You may choose to include `NimBLELog.h` in your appplication if you want to use the `NIMBLE_LOGx` macros for debugging.
These macros are used the same way as the `ESP_LOGx` macros.
<br/>
### Class Names
Class names remain the same as the original with the addition of a "Nim" prefix.
For example `BLEDevice` is now `NimBLEDevice` and `BLEServer` is now `NimBLEServer` etc.
For convienience definitions have been added to allow applications to use either name for all classes
this means **no class names need to be changed in existing code** and makes migrating easier.
<br/>
### BLE Addresses
`BLEAddress` (`NimBLEAddress`) When constructing an address the constructor now takes an *(optional)* `uint8_t type` paramameter
to specify the address type. Default is (0) Public static address.
For example `BLEAddress addr(11:22:33:44:55:66, 1)` will create the address object with an address type of: 1 (Random).
As this paramameter is optional no changes to existing code are needed, it is mentioned here for information.
<br/>
`BLEAddress::getNative` (`NimBLEAddress::getNative`) returns a uint8_t pointer to the native address byte array.
In this library the address bytes are stored in reverse order from the original library. This is due to the way
the NimBLE stack expects addresses to be presented to it. All other functions such as `toString` are
not affected as the endian change is made within them.
<br/>
<a name="server-api"></a>
## Server API
Creating a `BLEServer` instance is the same as original, no changes required.
For example `BLEDevice::createServer()` will work just as it did before.
`BLEServerCallbacks` (`NimBLEServerCallbacks`) has new methods for handling security operations.
**Note:** All callback methods have default implementations which allows the application to implement only the methods applicable.
<br/>
<a name="services"></a>
### Services
Creating a `BLEService` (`NimBLEService`) instance is the same as original, no changes required.
For example `BLEServer::createService(SERVICE_UUID)` will work just as it did before.
<a name="characteristics"></a>
### Characteristics
`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`.
#### Originally
> BLECharacteristic::PROPERTY_READ |
> BLECharacteristic::PROPERTY_WRITE
#### Is Now
> NIMBLE_PROPERTY::READ |
> NIMBLE_PROPERTY::WRITE
<br/>
#### The full list of properties
> NIMBLE_PROPERTY::READ
> NIMBLE_PROPERTY::READ_ENC
> NIMBLE_PROPERTY::READ_AUTHEN
> NIMBLE_PROPERTY::READ_AUTHOR
> NIMBLE_PROPERTY::WRITE
> NIMBLE_PROPERTY::WRITE_NR
> NIMBLE_PROPERTY::WRITE_ENC
> NIMBLE_PROPERTY::WRITE_AUTHEN
> NIMBLE_PROPERTY::WRITE_AUTHOR
> NIMBLE_PROPERTY::BROADCAST
> NIMBLE_PROPERTY::NOTIFY
> NIMBLE_PROPERTY::INDICATE
<br/>
**Example:**
```
BLECharacteristic *pCharacteristic = pService->createCharacteristic(
CHARACTERISTIC_UUID,
BLECharacteristic::PROPERTY_READ |
BLECharacteristic::PROPERTY_WRITE
);
```
Needs to be changed to:
```
BLECharacteristic *pCharacteristic = pService->createCharacteristic(
CHARACTERISTIC_UUID,
NIMBLE_PROPERTY::READ |
NIMBLE_PROPERTY::WRITE
);
```
<br/>
`BLECharacteristicCallbacks` (`NimBLECharacteristicCallbacks`) has a new method `NimBLECharacteristicCallbacks::onSubscribe`
which is called when a client subscribes to notifications/indications.
**Note:** All callback methods have default implementations which allows the application to implement only the methods applicable.
<br/>
> BLECharacteristic::getData
**Has been removed from the API.**
Originally this returned a `uint8_t*` to the internal data, which is volatile.
To prevent possibly throwing exceptions this has been removed and `NimBLECharacteristic::getValue` should be used
to get a copy of the data first which can then safely be accessed via pointer.
**Example:**
```
std::string value = pCharacteristic->getValue();
uint8_t *pData = (uint8_t*)value.data();
```
Alternatively use the `getValue` template:
```
my_struct_t myStruct = pChr->getValue<my_struct_t>();
```
<br/>
<a name="descriptors"></a>
### Descriptors
The previous method `BLECharacteristic::addDescriptor()` has been removed.
Descriptors are now created using the `NimBLECharacteristic::createDescriptor` method.
BLE2902 or NimBLE2902 class has been removed.
NimBLE automatically creates the 0x2902 descriptor if a characteristic has a notification or indication property assigned to it.
It was no longer useful to have a class for the 0x2902 descriptor as a new callback `NimBLECharacteristicCallbacks::onSubscribe` was added
to handle callback functionality and the client subscription status is handled internally.
**Note:** Attempting to create a 0x2902 descriptor will trigger an assert to notify the error,
allowing the creation of it would cause a fault in the NimBLE stack.
All other descriptors are now created just as characteristics are by using the `NimBLECharacteristic::createDescriptor` method (except 0x2904, see below).
Which are defined as:
```
NimBLEDescriptor* createDescriptor(const char* uuid,
uint32_t properties =
NIMBLE_PROPERTY::READ |
NIMBLE_PROPERTY::WRITE,
uint16_t max_len = 100);
NimBLEDescriptor* createDescriptor(NimBLEUUID uuid,
uint32_t properties =
NIMBLE_PROPERTY::READ |
NIMBLE_PROPERTY::WRITE,
uint16_t max_len = 100);
```
##### Example
```
pDescriptor = pCharacteristic->createDescriptor("ABCD",
NIMBLE_PROPERTY::READ |
NIMBLE_PROPERTY::WRITE |
NIMBLE_PROPERTY::WRITE_ENC,
25);
```
Would create a descriptor with the UUID 0xABCD, publicly readable but only writable if paired/bonded (encrypted) and has a max value length of 25 bytes.
<br/>
For the 0x2904, there is a special class that is created when you call `createDescriptor("2904").
The pointer returned is of the base class `NimBLEDescriptor` but the call will create the derived class of `NimBLE2904` so you must cast the returned pointer to
`NimBLE2904` to access the specific class methods.
##### Example
```
p2904 = (NimBLE2904*)pCharacteristic->createDescriptor("2904");
```
<br/>
<a name="server-security"></a>
### Server Security
Security is set on the characteristic or descriptor properties by applying one of the following:
> NIMBLE_PROPERTY::READ_ENC
> NIMBLE_PROPERTY::READ_AUTHEN
> NIMBLE_PROPERTY::READ_AUTHOR
> NIMBLE_PROPERTY::WRITE_ENC
> NIMBLE_PROPERTY::WRITE_AUTHEN
> NIMBLE_PROPERTY::WRITE_AUTHOR
When a peer wants to read or write a characteristic or descriptor with any of these properties applied
it will trigger the pairing process. By default the "just-works" pairing will be performed automatically.
This can be changed to use passkey authentication or numeric comparison. See [Security API](#security-api) for details.
<br/>
<a name="advertising-api"></a>
## Advertising API
Advertising works the same as the original API except:
> BLEAdvertising::setMinPreferred
> BLEAdvertising::setMaxPreferred
These methods were found to not provide useful functionality and consumed valuable advertising space (6 bytes of 31) if used unknowingly.
If you wish to advertise these parameters you can still do so manually via `BLEAdvertisementData::addData` (`NimBLEAdvertisementData::addData`).
<br/>
Calling `NimBLEAdvertising::setAdvertisementData` will entirely replace any data set with `NimBLEAdvertising::addServiceUUID`, or
`NimBLEAdvertising::setAppearance`. You should set all the data you wish to advertise within the `NimBLEAdvertisementData` instead.
Calling `NimBLEAdvertising::setScanResponseData` without also calling `NimBLEAdvertising::setAdvertisementData` will have no effect.
When using custom scan response data you must also use custom advertisement data.
<br/>
> BLEAdvertising::start (NimBLEAdvertising::start)
Now takes 2 optional parameters, the first is the duration to advertise for (in seconds), the second is a callback
that is invoked when advertsing ends and takes a pointer to a `NimBLEAdvertising` object (similar to the `NimBLEScan::start` API).
This provides an opportunity to update the advertisment data if desired.
<br/>
<a name="client-api"></a>
## Client API
Client instances are created just as before with `BLEDevice::createClient` (`NimBLEDevice::createClient`).
Multiple client instances can be created, up to the maximum number of connections set in the config file (default: 3).
To delete a client instance you must use `NimBLEDevice::deleteClient`.
`BLEClient::connect`(`NimBLEClient::connect`) Has had it's parameters altered.
Defined as:
> NimBLEClient::connect(bool deleteServices = true);
> NimBLEClient::connect(NimBLEAdvertisedDevice\* device, bool deleteServices = true);
> NimBLEClient::connect(NimBLEAddress address, bool deleteServices = true);
The type parameter has been removed and a new bool parameter has been added to indicate if the client should
delete the attribute database previously retrieved (if applicable) for the peripheral, default value is true.
If set to false the client will use the attribute database it retrieved from the peripheral when previously connected.
This allows for faster connections and power saving if the devices dropped connection and are reconnecting.
<br/>
> `BLEClient::getServices` (`NimBLEClient::getServices`)
This method now takes an optional (bool) parameter to indicate if the services should be retrieved from the server (true) or
the currently known database returned (false : default).
Also now returns a pointer to `std::vector` instead of `std::map`.
<br/>
**Removed:** the automatic discovery of all peripheral attributes as they consumed time and resources for data
the user may not be interested in.
**Added:** `NimBLEClient::discoverAttributes` for the user to discover all the peripheral attributes
to replace the the removed automatic functionality.
<br/>
<a name="remote-services"></a>
### Remote Services
`BLERemoteService` (`NimBLERemoteService`) Methods remain mostly unchanged with the exceptions of:
> BLERemoteService::getCharacteristicsByHandle
This method has been removed.
<br/>
> `BLERemoteService::getCharacteristics` (`NimBLERemoteService::getCharacteristics`)
This method now takes an optional (bool) parameter to indicate if the characteristics should be retrieved from the server (true) or
the currently known database returned (false : default).
Also now returns a pointer to `std::vector` instead of `std::map`.
<br/>
<a name="remote-characteristics"></a>
### Remote Characteristics
`BLERemoteCharacteristic` (`NimBLERemoteCharacteristic`) There have been a few changes to the methods in this class:
> `BLERemoteCharacteristic::writeValue` (`NimBLERemoteCharacteristic::writeValue`)
> `BLERemoteCharacteristic::registerForNotify` (`NimBLERemoteCharacteristic::registerForNotify`)
Now return true or false to indicate success or failure so you can choose to disconnect or try again.
<br/>
> `BLERemoteCharacteristic::registerForNotify` (`NimBLERemoteCharacteristic::registerForNotify`)
Is now **deprecated**.
> `NimBLERemoteCharacteristic::subscribe`
> `NimBLERemoteCharacteristic::unsubscribe`
Are the new methods added to replace it.
<br/>
> `BLERemoteCharacteristic::readUInt8` (`NimBLERemoteCharacteristic::readUInt8`)
> `BLERemoteCharacteristic::readUInt16` (`NimBLERemoteCharacteristic::readUInt16`)
> `BLERemoteCharacteristic::readUInt32` (`NimBLERemoteCharacteristic::readUInt32`)
> `BLERemoteCharacteristic::readFloat` (`NimBLERemoteCharacteristic::readFloat`)
Are **deprecated** a template: NimBLERemoteCharacteristic::readValue<type\>(time_t\*, bool) has been added to replace them.
<br/>
> `BLERemoteCharacteristic::readRawData`
**Has been removed from the API**
Originally it stored an unnecessary copy of the data and was returning a `uint8_t` pointer to volatile internal data.
The user application should use `NimBLERemoteCharacteristic::readValue` or `NimBLERemoteCharacteristic::getValue`.
To obatain a copy of the data, then cast the returned std::string to the type required such as:
```
std::string value = pChr->readValue();
uint8_t *data = (uint8_t*)value.data();
```
Alternatively use the `readValue` template:
```
my_struct_t myStruct = pChr->readValue<my_struct_t>();
```
<br/>
> `BLERemoteCharacteristic::getDescriptors` (`NimBLERemoteCharacteristic::getDescriptors`)
This method now takes an optional (bool) parameter to indicate if the descriptors should be retrieved from the server (true) or
the currently known database returned (false : default).
Also now returns a pointer to `std::vector` instead of `std::map`.
<br/>
<a name="client-security"></a>
### Client Security
The client will automatically initiate security when the peripheral responds that it's required.
The default configuration will use "just-works" pairing with no bonding, if you wish to enable bonding see below.
<br/>
<a name="security-api"></a>
## Security API
Security operations have been moved to `BLEDevice` (`NimBLEDevice`).
Also security callback methods are now incorporated in the `NimBLEServerCallbacks` / `NimBLEClientCallbacks` classes.
However backward compatibility with the original `BLESecurity` (`NimBLESecurity`) class is retained to minimize application code changes.
The callback methods are:
> `bool onConfirmPIN(uint32_t pin)`
Receives the pin when using numeric comparison authentication, `return true;` to accept.
<br/>
> `uint32_t onPassKeyRequest()`
For server callback; return the passkey expected from the client.
For client callback; return the passkey to send to the server.
<br/>
> `void onAuthenticationComplete(ble_gap_conn_desc\* desc)`
Authentication complete, success or failed information is in `desc`.
<br/>
Security settings and IO capabilities are now set by the following methods of NimBLEDevice.
> `NimBLEDevice::setSecurityAuth(bool bonding, bool mitm, bool sc)`
> `NimBLEDevice::setSecurityAuth(uint8_t auth_req)`
Sets the authorization mode for this device.
<br/>
> `NimBLEDevice::setSecurityIOCap(uint8_t iocap)`
Sets the Input/Output capabilities of this device.
<br/>
> `NimBLEDevice::setSecurityInitKey(uint8_t init_key)`
If we are the initiator of the security procedure this sets the keys we will distribute.
<br/>
> `NimBLEDevice::setSecurityRespKey(uint8_t resp_key)`
Sets the keys we are willing to accept from the peer during pairing.
<br/>
<a name="arduino-configuration"></a>
## Arduino Configuration
Unlike the original library pre-packaged in the esp32-arduino, this library has all the configuration
options that are normally set in menuconfig available in the *src/nimconfig.h* file.
This allows Arduino users to fully customize the build, such as increasing max connections
or loading the BLE stack into external PSRAM.
For details on the options, they are fully commented in *nimconfig.h*
<br/>

View File

@ -0,0 +1,339 @@
# New User Guide
**Note:** If you are migrating an existing project from the original Bluedroid library please see the [Migration Guide.](Migration_guide.md)
If you are a new user this will guide you through a simple server and client application.
* [Creating a Server](#creating-a-server)
* [Creating a Client](#creating-a-client)
<br/>
## Include Files
At the top of your application file add `#include NimBLEDevice.h`, this is the only header required and provides access to all classes.
<br/>
## Using the Library
In order to perform any BLE tasks you must first initialize the library, this prepares the NimBLE stack to be ready for commands.
To do this you must call `NimBLEDevice::init("your device name here")`, the parameter passed is a character string containing the name you want to advertise.
If you're not creating a server or do not want to advertise a name, simply pass an empty string for the parameter.
This can be called any time you wish to use BLE functions and does not need to be called from app_main(IDF) or setup(Arduino) but usually is.
<br/>
<a name="creating-a-server"></a>
## Creating a Server
BLE servers perform 2 tasks, they advertise their existance for clients to find them and they provide services which contain information for the connecting client.
After initializing the NimBLE stack we create a server by calling `NimBLEDevice::createServer()`, this will create a server instance and return a pointer to it.
Once we have created the server we need to tell it the services it hosts.
To do this we call `NimBLEServer::createService(const char* uuid)`. Which returns a pointer to an instance of `NimBLEService`.
The `uuid` parameter is a hexadecimal string with the uuid we want to give the service, it can be 16, 32, or 128 bits.
For this example we will keep it simple and use a 16 bit value: ABCD.
<br/>
**Example code:**
```
#include "NimBLEDevice.h"
// void setup() in Arduino
void app_main(void)
{
NimBLEDevice::init("NimBLE");
NimBLEServer *pServer = NimBLEDevice::createServer();
NimBLEService *pService = pServer->createService("ABCD");
}
```
Now we have NimBLE initialized, a server created and a service assigned to it.
We can't do much with this yet so now we should add a characteristic to the service to provide some data.
Next we call `NimBLEService::createCharacteristic` which returns a pointer to an instance of `NimBLECharacteristic`, and takes two parameters:
A `uuid` to specify the UUID of the characteristic and a bitmask of the properties we want applied to it.
Just as with the service UUID we will use a simple 16 bit value: 1234.
The properties bitmask is a little more involved. It is a combination of NIMBLE_PROPERTY:: values.
Here is the list of options:
> NIMBLE_PROPERTY\::READ
> NIMBLE_PROPERTY\::READ_ENC
> NIMBLE_PROPERTY\::READ_AUTHEN
> NIMBLE_PROPERTY\::READ_AUTHOR
> NIMBLE_PROPERTY\::WRITE
> NIMBLE_PROPERTY\::WRITE_NR
> NIMBLE_PROPERTY\::WRITE_ENC
> NIMBLE_PROPERTY\::WRITE_AUTHEN
> NIMBLE_PROPERTY\::WRITE_AUTHOR
> NIMBLE_PROPERTY\::BROADCAST
> NIMBLE_PROPERTY\::NOTIFY
> NIMBLE_PROPERTY\::INDICATE
For this example we won't need to specify these as the default value is `NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE`
which will allow reading and writing values to the characteristic without encryption or security.
The function call will simply be `pService->createCharacteristic("1234");`
<br/>
**Our example code now is:**
```
#include "NimBLEDevice.h"
// void setup() in Arduino
void app_main(void)
{
NimBLEDevice::init("NimBLE");
NimBLEServer *pServer = NimBLEDevice::createServer();
NimBLEService *pService = pServer->createService("ABCD");
NimBLECharacteristic *pCharacteristic = pService->createCharacteristic("1234");
}
```
All that's left to do now is start the sevice, give the characteristic a value and start advertising for clients.
Fist we start the service by calling `NimBLEService::start()`.
Next we need to call `NimBLECharacteristic::setValue` to set the characteristic value that the client will read.
There are many different types you can send as parameters for the value but for this example we will use a simple string.
`pCharacteristic->setValue("Hello BLE");`
Next we need to advertise for connections.
To do this we create an instance of `NimBLEAdvertising` add our service to it (optional) and start advertisng.
**The code for this will be:**
```
NimBLEAdvertising *pAdvertising = NimBLEDevice::getAdvertising(); // create advertising instance
pAdvertising->addServiceUUID("ABCD"); // tell advertising the UUID of our service
pAdvertising->start(); // start advertising
```
That's it, this will be enough to create a BLE server with a service and a characteristic and advertise for client connections.
**The full example code:**
```
#include "NimBLEDevice.h"
// void setup() in Arduino
void app_main(void)
{
NimBLEDevice::init("NimBLE");
NimBLEServer *pServer = NimBLEDevice::createServer();
NimBLEService *pService = pServer->createService("ABCD");
NimBLECharacteristic *pCharacteristic = pService->createCharacteristic("1234");
pService->start();
pCharacteristic->setValue("Hello BLE");
NimBLEAdvertising *pAdvertising = NimBLEDevice::getAdvertising();
pAdvertising->addServiceUUID("ABCD");
pAdvertising->start();
}
```
Now if you scan with your phone using nRFConnect or any other BLE app you should see a device named "NimBLE" with a service of "ABCD".
For more advanced features and options please see the server examples in the examples folder.
<br/>
<a name="creating-a-client"></a>
## Creating a Client
BLE clients perform 2 tasks, they scan for advertising servers and form connections to them to read and write to their characteristics/descriptors.
After initializing the NimBLE stack we create a scan instance by calling `NimBLEDevice::getScan()`, this will create a `NimBLEScan` instance and return a pointer to it.
Once we have created the scan we can start looking for advertising servers.
To do this we call `NimBLEScan::start(duration)`, the duration parameter is a uint32_t that specifies the number of seconds to scan for,
passing 0 will scan forever.
In this example we will scan for 10 seconds. This is a blocking function (a non blocking overload is also available).
This call returns an instance of `NimBLEScanResults` when the scan completes which can be parsed for advertisers we are interested in.
**Example Code:**
```
#include "NimBLEDevice.h"
// void setup() in Arduino
void app_main(void)
{
NimBLEDevice::init("");
NimBLEScan *pScan = NimBLEDevice::getScan();
NimBLEScanResults results = pScan->start(10);
}
```
<br/>
Now that we have scanned we need to check the results for any advertisers we are interested in connecting to.
To do this we iterate through the results and check if any of the devices found are advertising the service we want `ABCD`.
Each result in `NimBLEScanResults` is a `NimBLEAdvertisedDevice` instance that we can access data from.
We will check each device found for the `ABCD` service by calling `NimBLEAdvertisedDevice::isAdvertisingService`.
This takes an instance of `NimBLEUUID` as a parameter so we will need to create one.
**The code for this looks like:**
```
NimBLEUUID serviceUuid("ABCD");
for(int i = 0; i < results.getCount(); i++) {
NimBLEAdvertisedDevice device = results.getDevice(i);
if (device.isAdvertisingService(serviceUuid)) {
// create a client and connect
}
}
```
<br/>
Now that we can scan and parse advertisers we need to be able to create a `NimBLEClient` instance and use it to connect.
To do this we call `NimBLEDevice::createClient` which creates the `NimBLEClient` instance and returns a pointer to it.
After this we call `NimBLEClient::connect` to connect to the advertiser.
This takes a pointer to the `NimBLEAdvertisedDevice` and returns `true` if successful.
**Lets do that now:**
```
NimBLEUUID serviceUuid("ABCD");
for(int i = 0; i < results.getCount(); i++) {
NimBLEAdvertisedDevice device = results.getDevice(i);
if (device.isAdvertisingService(serviceUuid)) {
NimBLEClient *pClient = NimBLEDevice::createClient();
if(pClient->connect(&device)) {
//success
} else {
// failed to connect
}
}
}
```
As shown, the call to `NimBLEClient::connect` should have it's eturn value tested to make sure it succeeded before proceeding to get data.
<br/>
Next we need to access the servers data by asking it for the service and the characteristic we are interested in, then read the characteristic value.
To do this we call `NimBLEClient::getService`, which takes as a parameter the UUID of the service and returns
a pointer an instance to `NimBLERemoteService` or `nullptr` if the service was not found.
Next we will call `NimBLERemoteService::getCharateristic` which takes as a parameter the UUID of the service and returns
a pointer to an instance of `NimBLERemoteCharacteristic` or `nullptr` if not found.
Finally we will read the characteristic value with `NimBLERemoteCharacteristic::readValue()`.
**Here is what that looks like:**
```
NimBLEUUID serviceUuid("ABCD");
for(int i = 0; i < results.getCount(); i++) {
NimBLEAdvertisedDevice device = results.getDevice(i);
if (device.isAdvertisingService(serviceUuid)) {
NimBLEClient *pClient = NimBLEDevice::createClient();
if (pClient->connect(&device)) {
NimBLERemoteService *pService = pClient->getService(serviceUuid);
if (pService != nullptr) {
NimBLERemoteCharacteristic *pCharacteristic = pService->getCharacteristic("1234");
if (pCharacteristic != nullptr) {
std::string value = pCharacteristic->readValue();
// print or do whatever you need with the value
}
}
} else {
// failed to connect
}
}
}
```
<br/>
The last thing we should do is clean up once we are done with the connection.
Because multiple clients are supported and can be created we should delete them when finished with them to conserve resources.
This is done by calling `NimBLEDevice::deleteClient`.
**Lets add that now:**
```
NimBLEUUID serviceUuid("ABCD");
for(int i = 0; i < results.getCount(); i++) {
NimBLEAdvertisedDevice device = results.getDevice(i);
if (device.isAdvertisingService(serviceUuid)) {
NimBLEClient *pClient = NimBLEDevice::createClient();
if (pClient->connect(&device)) {
NimBLERemoteService *pService = pClient->getService(serviceUuid);
if (pService != nullptr) {
NimBLERemoteCharacteristic *pCharacteristic = pService->getCharacteristic("1234");
if (pCharacteristic != nullptr) {
std::string value = pCharacteristic->readValue();
// print or do whatever you need with the value
}
}
} else {
// failed to connect
}
NimBLEDevice::deleteClient(pClient);
}
}
```
Note that there is no need to disconnect as that will be done when deleting the client instance.
<br/>
**Here is the full example code:**
```
#include "NimBLEDevice.h"
// void setup() in Arduino
void app_main(void)
{
NimBLEDevice::init("");
NimBLEScan *pScan = NimBLEDevice::getScan();
NimBLEScanResults results = pScan->start(10);
NimBLEUUID serviceUuid("ABCD");
for(int i = 0; i < results.getCount(); i++) {
NimBLEAdvertisedDevice device = results.getDevice(i);
if (device.isAdvertisingService(serviceUuid)) {
NimBLEClient *pClient = NimBLEDevice::createClient();
if (pClient->connect(&device)) {
NimBLERemoteService *pService = pClient->getService(serviceUuid);
if (pService != nullptr) {
NimBLERemoteCharacteristic *pCharacteristic = pService->getCharacteristic("1234");
if (pCharacteristic != nullptr) {
std::string value = pCharacteristic->readValue();
// print or do whatever you need with the value
}
}
} else {
// failed to connect
}
NimBLEDevice::deleteClient(pClient);
}
}
}
```
<br/>
For more advanced features and options please see the client examples in the examples folder.
<br/>

View File

@ -107,9 +107,10 @@ class AdvertisedDeviceCallbacks: public NimBLEAdvertisedDeviceCallbacks {
void notifyCB(NimBLERemoteCharacteristic* pRemoteCharacteristic, uint8_t* pData, size_t length, bool isNotify){
std::string str = (isNotify == true) ? "Notification" : "Indication";
str += " from ";
str += pRemoteCharacteristic->getRemoteService()->getClient()->getPeerAddress().toString();
str += ": Service = " + pRemoteCharacteristic->getRemoteService()->getUUID().toString();
str += ", Characteristic = " + pRemoteCharacteristic->getUUID().toString();
/** NimBLEAddress and NimBLEUUID have std::string operators */
str += std::string(pRemoteCharacteristic->getRemoteService()->getClient()->getPeerAddress());
str += ": Service = " + std::string(pRemoteCharacteristic->getRemoteService()->getUUID());
str += ", Characteristic = " + std::string(pRemoteCharacteristic->getUUID());
str += ", Value = " + std::string((char*)pData, length);
Serial.println(str.c_str());
}
@ -228,17 +229,22 @@ bool connectToServer() {
}
}
/** registerForNotify() has been deprecated and replaced with subscribe() / unsubscribe().
* Subscribe parameter defaults are: notifications=true, notifyCallback=nullptr, response=false.
* Unsubscribe parameter defaults are: response=false.
*/
if(pChr->canNotify()) {
/** Must send a callback to subscribe, if nullptr it will unsubscribe */
if(!pChr->registerForNotify(notifyCB)) {
//if(!pChr->registerForNotify(notifyCB)) {
if(!pChr->subscribe(true, notifyCB)) {
/** Disconnect if subscribe failed */
pClient->disconnect();
return false;
}
}
else if(pChr->canIndicate()) {
/** Send false as second argument to subscribe to indications instead of notifications */
if(!pChr->registerForNotify(notifyCB, false)) {
/** Send false as first argument to subscribe to indications instead of notifications */
//if(!pChr->registerForNotify(notifyCB, false)) {
if(!pChr->subscribe(false, notifyCB)) {
/** Disconnect if subscribe failed */
pClient->disconnect();
return false;
@ -289,17 +295,22 @@ bool connectToServer() {
}
}
/** registerForNotify() has been deprecated and replaced with subscribe() / unsubscribe().
* Subscribe parameter defaults are: notifications=true, notifyCallback=nullptr, response=false.
* Unsubscribe parameter defaults are: response=false.
*/
if(pChr->canNotify()) {
/** Must send a callback to subscribe, if nullptr it will unsubscribe */
if(!pChr->registerForNotify(notifyCB)) {
//if(!pChr->registerForNotify(notifyCB)) {
if(!pChr->subscribe(true, notifyCB)) {
/** Disconnect if subscribe failed */
pClient->disconnect();
return false;
}
}
else if(pChr->canIndicate()) {
/** Send false as second argument to subscribe to indications instead of notifications */
if(!pChr->registerForNotify(notifyCB, false)) {
/** Send false as first argument to subscribe to indications instead of notifications */
//if(!pChr->registerForNotify(notifyCB, false)) {
if(!pChr->subscribe(false, notifyCB)) {
/** Disconnect if subscribe failed */
pClient->disconnect();
return false;

View File

@ -9,8 +9,6 @@
*/
#include <NimBLEDevice.h>
#include <NimBLE2904.h>
#include <NimBLE2902.h>
static NimBLEServer* pServer;
@ -102,24 +100,33 @@ class CharacteristicCallbacks: public NimBLECharacteristicCallbacks {
str += NimBLEUtils::returnCodeToString(code);
Serial.println(str);
};
void onSubscribe(NimBLECharacteristic* pCharacteristic, ble_gap_conn_desc* desc, uint16_t subValue) {
String str = "Client ID: ";
str += desc->conn_handle;
str += " Address: ";
str += std::string(NimBLEAddress(desc->peer_ota_addr)).c_str();
if(subValue == 0) {
str += " Unsubscribed to ";
}else if(subValue == 1) {
str += " Subscribed to notfications for ";
} else if(subValue == 2) {
str += " Subscribed to indications for ";
} else if(subValue == 3) {
str += " Subscribed to notifications and indications for ";
}
str += std::string(pCharacteristic->getUUID()).c_str();
Serial.println(str);
};
};
/** Handler class for descriptor actions */
class DescriptorCallbacks : public NimBLEDescriptorCallbacks {
void onWrite(NimBLEDescriptor* pDescriptor) {
if(pDescriptor->getUUID().equals(NimBLEUUID("2902"))) {
/** Cast to NimBLE2902 to use the class specific functions. **/
NimBLE2902* p2902 = (NimBLE2902*)pDescriptor;
if(p2902->getNotifications()) {
Serial.println("Client Subscribed to notfications");
} else {
Serial.println("Client Unubscribed to notfications");
}
} else {
std::string dscVal((char*)pDescriptor->getValue(), pDescriptor->getLength());
Serial.print("Descriptor witten value:");
Serial.println(dscVal.c_str());
}
std::string dscVal((char*)pDescriptor->getValue(), pDescriptor->getLength());
Serial.print("Descriptor witten value:");
Serial.println(dscVal.c_str());
};
void onRead(NimBLEDescriptor* pDescriptor) {
@ -176,9 +183,9 @@ void setup() {
pBeefCharacteristic->setValue("Burger");
pBeefCharacteristic->setCallbacks(&chrCallbacks);
/** 2902 and 2904 descriptors are a special case, when createDescriptor is called with
* either of those uuid's it will create the associated class with the correct properties
* and sizes. However we must cast the returned reference to the correct type as the method
/** 2904 descriptors are a special case, when createDescriptor is called with
* 0x2904 a NimBLE2904 class is created with the correct properties and sizes.
* However we must cast the returned reference to the correct type as the method
* only returns a pointer to the base NimBLEDescriptor class.
*/
NimBLE2904* pBeef2904 = (NimBLE2904*)pBeefCharacteristic->createDescriptor("2904");
@ -197,6 +204,10 @@ void setup() {
pFoodCharacteristic->setValue("Fries");
pFoodCharacteristic->setCallbacks(&chrCallbacks);
/** Note a 0x2902 descriptor MUST NOT be created as NimBLE will create one automatically
* if notification or indication properties are assigned to a characteristic.
*/
/** Custom descriptor: Arguments are UUID, Properties, max length in bytes of the value */
NimBLEDescriptor* pC01Ddsc = pFoodCharacteristic->createDescriptor(
"C01D",
@ -208,14 +219,6 @@ void setup() {
pC01Ddsc->setValue("Send it back!");
pC01Ddsc->setCallbacks(&dscCallbacks);
/** Note a 2902 descriptor does NOT need to be created as any chactateristic with
* notification or indication properties will have one created autmatically.
* Manually creating it is only useful if you wish to handle callback functions
* as shown here. Otherwise this can be removed without loss of functionality.
*/
NimBLE2902* pFood2902 = (NimBLE2902*)pFoodCharacteristic->createDescriptor("2902");
pFood2902->setCallbacks(&dscCallbacks);
/** Start the services when finished creating all Characteristics and Descriptors */
pDeadService->start();
pBaadService->start();
@ -247,4 +250,4 @@ void loop() {
}
delay(2000);
}
}

View File

@ -102,16 +102,13 @@ void setup() {
// https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.client_characteristic_configuration.xml
// Create a BLE Descriptor
/*********** New createDescriptor method ************
NOTE: There is no need to create the 2902 descriptor
as it will be created automatically if notifications
/***************************************************
NOTE: DO NOT create a 2902 descriptor.
it will be created automatically if notifications
or indications are enabled on a characteristic.
pCharacteristic->addDescriptor(new BLE2902());
****************************************************/
/** Add properties the same way as characteristics now **/
pCharacteristic->createDescriptor("2902" /** , NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE **/);
// Start the service
pService->start();
@ -119,7 +116,9 @@ void setup() {
BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
pAdvertising->addServiceUUID(SERVICE_UUID);
pAdvertising->setScanResponse(false);
pAdvertising->setMinPreferred(0x0); // set value to 0x00 to not advertise this parameter
/**This method is removed as it was no longer useful and consumed advertising space
* pAdvertising->setMinPreferred(0x0); // set value to 0x00 to not advertise this parameter
*/
BLEDevice::startAdvertising();
Serial.println("Waiting a client connection to notify...");
}

View File

@ -44,8 +44,10 @@ void setup() {
BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
pAdvertising->addServiceUUID(SERVICE_UUID);
pAdvertising->setScanResponse(true);
pAdvertising->setMinPreferred(0x06); // functions that help with iPhone connections issue
pAdvertising->setMinPreferred(0x12);
/**These methods are removed as they are no longer useful and consumed advertising space
* pAdvertising->setMinPreferred(0x06); // functions that help with iPhone connections issue
* pAdvertising->setMinPreferred(0x12);
*/
BLEDevice::startAdvertising();
Serial.println("Characteristic defined! Now you can read it in your phone!");
}

View File

@ -105,16 +105,13 @@ void setup() {
// https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.client_characteristic_configuration.xml
// Create a BLE Descriptor
/*********** New createDescriptor method ************
NOTE: There is no need to create the 2902 descriptor
as it will be created automatically if notifications
/***************************************************
NOTE: DO NOT create a 2902 descriptor
it will be created automatically if notifications
or indications are enabled on a characteristic.
pCharacteristic->addDescriptor(new BLE2902());
****************************************************/
/** Add properties the same way as characteristics now **/
pCharacteristic->createDescriptor("2902" /** , NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE **/);
// Start the service
pService->start();
@ -123,7 +120,9 @@ void setup() {
BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
pAdvertising->addServiceUUID(SERVICE_UUID);
pAdvertising->setScanResponse(false);
pAdvertising->setMinPreferred(0x0); // set value to 0x00 to not advertise this parameter
/**This method is removed it was no longer useful and consumed advertising space
* pAdvertising->setMinPreferred(0x0); // set value to 0x00 to not advertise this parameter
*/
BLEDevice::startAdvertising();
Serial.println("Waiting a client connection to notify...");
}

View File

@ -112,15 +112,13 @@ void setup() {
NIMBLE_PROPERTY::NOTIFY
);
/******* New createDescriptor method ********
NOTE: There is no need to create the 2902 descriptor
as it will be created automatically if notifications or
indications are enabled on a characteristic.
/***************************************************
NOTE: DO NOT create a 2902 descriptor
it will be created automatically if notifications
or indications are enabled on a characteristic.
pCharacteristic->addDescriptor(new BLE2902());
********************************************/
/** Add properties the same way as characteristics now **/
pTxCharacteristic->createDescriptor("2902" /** , NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE **/);
pCharacteristic->addDescriptor(new BLE2902());
****************************************************/
BLECharacteristic * pRxCharacteristic = pService->createCharacteristic(
CHARACTERISTIC_UUID_RX,

View File

@ -1,9 +1,9 @@
name=NimBLE-Arduino
version=0.9.0
author=H2zero
version=1.0.2
author=h2zero
maintainer=h2zero <powellperalta@gmail.com>
sentence=NimBLE library for Arduino
paragraph=A lighter-weight alternative to the bluedroid library for esp32.
sentence=Bluetooth low energy (BLE) library for arduino-esp32 based on NimBLE.
paragraph=This is a more updated and lower resource alternative to the original bluedroid BLE library for esp32. Uses 50% less flash space and approximately 100KB less ram with the same functionality. Nearly 100% compatible with existing application code, migration guide included.
url=https://github.com/h2zero/NimBLE-Arduino
category=Communication
architectures=esp32

View File

@ -114,6 +114,10 @@ bool FreeRTOS::Semaphore::timedWait(std::string owner, uint32_t timeoutMs) {
} // 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) {
@ -140,8 +144,7 @@ FreeRTOS::Semaphore::~Semaphore() {
/**
* @brief Give a semaphore.
* The Semaphore is given.
* @brief Give the semaphore.
*/
void FreeRTOS::Semaphore::give() {
NIMBLE_LOGD(LOG_TAG, "Semaphore giving: %s", toString().c_str());

View File

@ -29,6 +29,9 @@ public:
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>");
@ -42,6 +45,10 @@ public:
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:
@ -57,7 +64,7 @@ public:
/**
* @brief Ringbuffer.
* @brief A wrapper class for a freeRTOS ringbuffer.
*/
class Ringbuffer {
public:

View File

@ -1,79 +0,0 @@
/*
* NimBLE2902.cpp
*
* Created: on March 10, 2020
* Author H2zero
*
* Originally:
*
* BLE2902.cpp
*
* Created on: Jun 25, 2017
* Author: kolban
*/
/*
* See also:
* https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.client_characteristic_configuration.xml
*/
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include "nimconfig.h"
#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#include "NimBLE2902.h"
NimBLE2902::NimBLE2902(NimBLECharacteristic* pCharacterisitic)
: NimBLEDescriptor(NimBLEUUID((uint16_t) 0x2902),
BLE_GATT_CHR_F_READ |
BLE_GATT_CHR_F_WRITE,
2, pCharacterisitic)
{
uint8_t data[2] = { 0, 0 };
setValue(data, 2);
} // NimBLE2902
/**
* @brief Get the notifications value.
* @return The notifications value. True if notifications are enabled and false if not.
*/
bool NimBLE2902::getNotifications() {
return (getValue()[0] & (1 << 0)) != 0;
} // getNotifications
/**
* @brief Get the indications value.
* @return The indications value. True if indications are enabled and false if not.
*/
bool NimBLE2902::getIndications() {
return (getValue()[0] & (1 << 1)) != 0;
} // getIndications
/**
* @brief Set the indications flag.
* @param [in] flag The indications flag.
*/
void NimBLE2902::setIndications(bool flag) {
uint8_t *pValue = getValue();
if (flag) pValue[0] |= 1 << 1;
else pValue[0] &= ~(1 << 1);
} // setIndications
/**
* @brief Set the notifications flag.
* @param [in] flag The notifications flag.
*/
void NimBLE2902::setNotifications(bool flag) {
uint8_t *pValue = getValue();
if (flag) pValue[0] |= 1 << 0;
else pValue[0] &= ~(1 << 0);
} // setNotifications
#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#endif

View File

@ -1,59 +0,0 @@
/*
* NimBLE2902.h
*
* Created: on March 10, 2020
* Author H2zero
*
* Originally:
*
* BLE2902.h
*
* Created on: Jun 25, 2017
* Author: kolban
*/
#ifndef MAIN_NIMBLE2902_H_
#define MAIN_NIMBLE2902_H_
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include "nimconfig.h"
#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#include "NimBLEDescriptor.h"
#include <vector>
#define NIMBLE_DESC_FLAG_NOTIFY 0x0001
#define NIMBLE_DESC_FLAG_INDICATE 0x0002
typedef struct {
uint16_t conn_id;
uint16_t sub_val;
} chr_sub_status_t;
/**
* @brief Descriptor for Client Characteristic Configuration.
*
* This is a convenience descriptor for the Client Characteristic Configuration which has a UUID of 0x2902.
*
* See also:
* https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.client_characteristic_configuration.xml
*/
class NimBLE2902: public NimBLEDescriptor {
public:
bool getNotifications();
bool getIndications();
void setNotifications(bool flag);
void setIndications(bool flag);
private:
NimBLE2902(NimBLECharacteristic* pCharacterisitic);
friend class NimBLECharacteristic;
std::vector<chr_sub_status_t> m_subscribedVec;
}; // NimBLE2902
#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#endif /* CONFIG_BT_ENABLED */
#endif /* MAIN_NIMBLE2902_H_ */

View File

@ -23,17 +23,26 @@
static const char* LOG_TAG = "NimBLEAddress";
/*************************************************
NOTE: NimBLE addresses are in INVERSE ORDER!
We will accomodate that fact in these methods.
* NOTE: NimBLE address bytes are in INVERSE ORDER!
* We will accomodate that fact in these methods.
*************************************************/
/**
* @brief Create an address from the native ESP32 representation.
* @param [in] address The native representation.
* @brief Create an address from the native NimBLE representation.
* @param [in] address The native NimBLE address.
*/
NimBLEAddress::NimBLEAddress(ble_addr_t address) {
memcpy(m_address, address.val, 6);
} // BLEAddress
m_addrType = address.type;
} // NimBLEAddress
/**
* @brief Create a blank address, i.e. 00:00:00:00:00:00, type 0.
*/
NimBLEAddress::NimBLEAddress() {
NimBLEAddress("");
} // NimBLEAddress
/**
@ -45,9 +54,12 @@ NimBLEAddress::NimBLEAddress(ble_addr_t address) {
* ```
* which is 17 characters in length.
*
* @param [in] stringAddress The hex representation of the address.
* @param [in] stringAddress The hex string representation of the address.
* @param [in] type The type of the address.
*/
NimBLEAddress::NimBLEAddress(const std::string &stringAddress) {
NimBLEAddress::NimBLEAddress(const std::string &stringAddress, uint8_t type) {
m_addrType = type;
if (stringAddress.length() == 0) {
memset(m_address, 0, 6);
return;
@ -72,24 +84,29 @@ NimBLEAddress::NimBLEAddress(const std::string &stringAddress) {
for(size_t index = 0; index < sizeof m_address; index++) {
m_address[index] = data[index];
}
} // BLEAddress
/**
* @brief Constructor for compatibility with bluedroid esp library.
* @param [in] uint8_t[6] or esp_bd_addr_t struct containing the address.
*/
NimBLEAddress::NimBLEAddress(uint8_t address[6]) {
std::reverse_copy(address, address + sizeof m_address, m_address);
} // NimBLEAddress
/**
* @brief Constructor for address using a hex value. Use the same byte order, so use 0xa4c1385def16 for "a4:c1:38:5d:ef:16"
* @param [in] uint64_t containing the address.
* @brief Constructor for compatibility with bluedroid esp library using native ESP representation.
* @param [in] address A uint8_t[6] or esp_bd_addr_t containing the address.
* @param [in] type The type of the address.
*/
NimBLEAddress::NimBLEAddress(const uint64_t &address) {
NimBLEAddress::NimBLEAddress(uint8_t address[6], uint8_t type) {
std::reverse_copy(address, address + sizeof m_address, m_address);
m_addrType = type;
} // NimBLEAddress
/**
* @brief Constructor for address using a hex value.\n
* Use the same byte order, so use 0xa4c1385def16 for "a4:c1:38:5d:ef:16"
* @param [in] address uint64_t containing the address.
* @param [in] type The type of the address.
*/
NimBLEAddress::NimBLEAddress(const uint64_t &address, uint8_t type) {
memcpy(m_address, &address, sizeof m_address);
m_addrType = type;
} // NimBLEAddress
@ -104,14 +121,23 @@ bool NimBLEAddress::equals(const NimBLEAddress &otherAddress) const {
/**
* @brief Return the native representation of the address.
* @return The native representation of the address.
* @brief Get the native representation of the address.
* @return a pointer to the uint8_t[6] array of the address.
*/
const uint8_t *NimBLEAddress::getNative() const {
return m_address;
} // getNative
/**
* @brief Get the address type.
* @return The address type.
*/
uint8_t NimBLEAddress::getType() const {
return m_addrType;
} // getType
/**
* @brief Convert a BLE address to a string.
*
@ -122,30 +148,50 @@ const uint8_t *NimBLEAddress::getNative() const {
* ```
*
* @return The string representation of the address.
* @deprecated Use std::string() operator instead.
*/
std::string NimBLEAddress::toString() const {
return std::string(*this);
} // toString
/**
* @brief Convienience operator to check if this address is equal to another.
*/
bool NimBLEAddress::operator ==(const NimBLEAddress & rhs) const {
return memcmp(rhs.m_address, m_address, sizeof m_address) == 0;
}
} // operator ==
/**
* @brief Convienience operator to check if this address is not equal to another.
*/
bool NimBLEAddress::operator !=(const NimBLEAddress & rhs) const {
return !this->operator==(rhs);
}
} // operator !=
/**
* @brief Convienience operator to convert this address to string representation.
* @details This allows passing NimBLEAddress to functions
* that accept std::string and/or or it's methods as a parameter.
*/
NimBLEAddress::operator std::string() const {
char buffer[18];
sprintf(buffer, "%02x:%02x:%02x:%02x:%02x:%02x", m_address[5], m_address[4], m_address[3], m_address[2], m_address[1], m_address[0]);
snprintf(buffer, sizeof(buffer), "%02x:%02x:%02x:%02x:%02x:%02x",
m_address[5], m_address[4], m_address[3],
m_address[2], m_address[1], m_address[0]);
return std::string(buffer);
}
} // operator std::string
/**
* @brief Convienience operator to convert the native address representation to uint_64.
*/
NimBLEAddress::operator uint64_t() const {
uint64_t address = 0;
memcpy(&address, m_address, sizeof m_address);
return address;
}
} // operator uint64_t
#endif

View File

@ -33,13 +33,15 @@
*/
class NimBLEAddress {
public:
NimBLEAddress();
NimBLEAddress(ble_addr_t address);
NimBLEAddress(uint8_t address[6]);
NimBLEAddress(const std::string &stringAddress);
NimBLEAddress(const uint64_t &address);
bool equals(const NimBLEAddress &otherAddress) const;
const uint8_t* getNative() const;
std::string toString() const;
NimBLEAddress(uint8_t address[6], uint8_t type = BLE_ADDR_PUBLIC);
NimBLEAddress(const std::string &stringAddress, uint8_t type = BLE_ADDR_PUBLIC);
NimBLEAddress(const uint64_t &address, uint8_t type = BLE_ADDR_PUBLIC);
bool equals(const NimBLEAddress &otherAddress) const;
const uint8_t* getNative() const;
std::string toString() const;
uint8_t getType() const;
bool operator ==(const NimBLEAddress & rhs) const;
bool operator !=(const NimBLEAddress & rhs) const;
@ -48,6 +50,7 @@ public:
private:
uint8_t m_address[6];
uint8_t m_addrType;
};
#endif /* CONFIG_BT_ENABLED */

View File

@ -17,6 +17,7 @@
#include "nimconfig.h"
#if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
#include "NimBLEDevice.h"
#include "NimBLEAdvertisedDevice.h"
#include "NimBLEUtils.h"
#include "NimBLELog.h"
@ -30,13 +31,10 @@ static const char* LOG_TAG = "NimBLEAdvertisedDevice";
NimBLEAdvertisedDevice::NimBLEAdvertisedDevice() {
m_advType = 0;
m_appearance = 0;
m_deviceType = 0;
m_manufacturerData = "";
m_name = "";
m_rssi = -9999;
m_serviceData = "";
m_txPower = 0;
m_pScan = nullptr;
m_payloadLength = 0;
m_payload = nullptr;
@ -53,11 +51,7 @@ NimBLEAdvertisedDevice::NimBLEAdvertisedDevice() {
/**
* @brief Get the address.
*
* Every %BLE device exposes an address that is used to identify it and subsequently connect to it.
* Call this function to obtain the address of the advertised device.
*
* @brief Get the address of the advertising device.
* @return The address of the advertised device.
*/
NimBLEAddress NimBLEAdvertisedDevice::getAddress() {
@ -66,13 +60,17 @@ NimBLEAddress NimBLEAdvertisedDevice::getAddress() {
/**
* @brief Get the advertised type.
*
* @return The advertised type of the advertised device.
* @brief Get the advertisement type.
* @return The advertising type the device is reporting:
* * BLE_HCI_ADV_TYPE_ADV_IND (0) - indirect advertising
* * BLE_HCI_ADV_TYPE_ADV_DIRECT_IND_HD (1) - direct advertisng - high duty cycle
* * BLE_HCI_ADV_TYPE_ADV_SCAN_IND (2) - indirect scan response
* * BLE_HCI_ADV_TYPE_ADV_NONCONN_IND (3) - indirect advertisng - not connectable
* * BLE_HCI_ADV_TYPE_ADV_DIRECT_IND_LD (4) - direct advertising - low duty cycle
*/
uint8_t NimBLEAdvertisedDevice::getAdvType() {
return m_advType;
} // getAddress
} // getAdvType
/**
@ -98,7 +96,7 @@ std::string NimBLEAdvertisedDevice::getManufacturerData() {
/**
* @brief Get the name.
* @brief Get the advertised name.
* @return The name of the advertised device.
*/
std::string NimBLEAdvertisedDevice::getName() {
@ -116,54 +114,100 @@ int NimBLEAdvertisedDevice::getRSSI() {
/**
* @brief Get the scan object that created this advertisement.
* @brief Get the scan object that created this advertised device.
* @return The scan object.
*/
NimBLEScan* NimBLEAdvertisedDevice::getScan() {
return m_pScan;
return NimBLEDevice::getScan();
} // getScan
/**
* @brief Get the service data.
* @return The ServiceData of the advertised device.
* @param [in] index The vector index of the service data requested.
* @return The advertised service data or empty string if no data.
*/
std::string NimBLEAdvertisedDevice::getServiceData() {
return m_serviceData;
std::string NimBLEAdvertisedDevice::getServiceData(uint8_t index) {
if(index > m_serviceDataVec.size()) {
NIMBLE_LOGW(LOG_TAG, "getServiceData: index out of range");
return "";
}
return m_serviceDataVec[index].second;
} //getServiceData
/**
* @brief Get the service data UUID.
* @return The service data UUID.
* @brief Get the service data.
* @param [in] uuid The uuid of the service data requested.
* @return The advertised service data or empty string if no data.
*/
std::string NimBLEAdvertisedDevice::getServiceData(const NimBLEUUID &uuid) const {
for(auto &it : m_serviceDataVec) {
if(it.first == uuid) {
return it.second;
}
}
NIMBLE_LOGW(LOG_TAG, "getServiceData: uuid not found");
return "";
} //getServiceData
NimBLEUUID NimBLEAdvertisedDevice::getServiceDataUUID() {
return m_serviceDataUUID;
/**
* @brief Get the advertised service UUID.
* @param [in] index The vector index of the service data UUID requested.
* @return The advertised service UUID or an empty UUID if not found.
*/
NimBLEUUID NimBLEAdvertisedDevice::getServiceDataUUID(uint8_t index) {
if(!haveServiceData() || index > m_serviceDataVec.size()) {
NIMBLE_LOGW(LOG_TAG, "getServiceDataUUID: index out of range");
return NimBLEUUID("");
}
return m_serviceDataVec[index].first;
} // getServiceDataUUID
/**
* @brief Get the Service UUID.
* @return The Service UUID of the advertised device.
* @brief Get the count of advertised service data UUIDS
* @return The number of service data UUIDS in the vector.
*/
size_t NimBLEAdvertisedDevice::getServiceDataCount() {
return m_serviceDataVec.size();
} // getServiceDataCount
NimBLEUUID NimBLEAdvertisedDevice::getServiceUUID() { //TODO Remove it eventually, is no longer useful
return m_serviceUUIDs[0];
/**
* @brief Get the Service UUID.
* @param [in] index The vector index of the service UUID requested.
* @return The Service UUID of the advertised service, or an empty UUID if not found.
*/
NimBLEUUID NimBLEAdvertisedDevice::getServiceUUID(uint8_t index) {
if(!haveServiceUUID() || index > m_serviceUUIDs.size()) {
NIMBLE_LOGW(LOG_TAG, "getServiceUUID: index out of range");
return NimBLEUUID("");
}
return m_serviceUUIDs[index];
} // getServiceUUID
/**
* @brief Check advertised serviced for existence required UUID
* @brief Get the number of services advertised
* @return The count of services in the advertising packet.
*/
size_t NimBLEAdvertisedDevice::getServiceUUIDCount() {
return m_serviceUUIDs.size();
} // getServiceUUIDCount
/**
* @brief Check advertised services for existance of the required UUID
* @return Return true if service is advertised
*/
bool NimBLEAdvertisedDevice::isAdvertisingService(const NimBLEUUID &uuid){
bool NimBLEAdvertisedDevice::isAdvertisingService(const NimBLEUUID &uuid) const {
for (int i = 0; i < m_serviceUUIDs.size(); i++) {
NIMBLE_LOGI(LOG_TAG, "Comparing UUIDS: %s %s", m_serviceUUIDs[i].toString().c_str(), uuid.toString().c_str());
if (m_serviceUUIDs[i].equals(uuid)) return true;
}
return false;
}
} // isAdvertisingService
/**
@ -250,121 +294,126 @@ bool NimBLEAdvertisedDevice::haveTXPower() {
*
* https://www.bluetooth.com/specifications/assigned-numbers/generic-access-profile
*/
void NimBLEAdvertisedDevice::parseAdvertisement(ble_hs_adv_fields *fields) {
//char s[BLE_HS_ADV_MAX_SZ];
uint8_t *u8p;
uint8_t length;
int i;
void NimBLEAdvertisedDevice::parseAdvertisement(uint8_t* payload, uint8_t length) {
struct ble_hs_adv_fields fields;
int rc = ble_hs_adv_parse_fields(&fields, payload, length);
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "Gap Event Parse ERROR.");
return;
}
if (fields->uuids16 != NULL) {
for (i = 0; i < fields->num_uuids16; i++) {
setServiceUUID(NimBLEUUID(fields->uuids16[i].value));
m_payload = payload;
m_payloadLength = length;
#if CONFIG_LOG_DEFAULT_LEVEL > 3 || (ARDUINO_ARCH_ESP32 && CORE_DEBUG_LEVEL >= 4)
char* pHex = NimBLEUtils::buildHexData(nullptr, m_payload, m_payloadLength);
NIMBLE_LOGD(LOG_TAG,"payload: %s", pHex);
free(pHex);
#endif
if (fields.uuids16 != NULL) {
for (int i = 0; i < fields.num_uuids16; i++) {
setServiceUUID(NimBLEUUID(fields.uuids16[i].value));
}
}
if (fields->uuids32 != NULL) {
for (i = 0; i < fields->num_uuids32; i++) {
setServiceUUID(NimBLEUUID(fields->uuids32[i].value));
if (fields.uuids32 != NULL) {
for (int i = 0; i < fields.num_uuids32; i++) {
setServiceUUID(NimBLEUUID(fields.uuids32[i].value));
}
}
if (fields->uuids128 != NULL) {
for (i = 0; i < fields->num_uuids128; i++) {
setServiceUUID(NimBLEUUID(&fields->uuids128[i]));
if (fields.uuids128 != NULL) {
for (int i = 0; i < fields.num_uuids128; i++) {
setServiceUUID(NimBLEUUID(&fields.uuids128[i]));
}
}
if (fields->name != NULL) {
setName(std::string(reinterpret_cast<char*>(fields->name), fields->name_len));
if (fields.name != NULL) {
setName(std::string(reinterpret_cast<char*>(fields.name), fields.name_len));
}
if (fields->tx_pwr_lvl_is_present) {
setTXPower(fields->tx_pwr_lvl);
if (fields.tx_pwr_lvl_is_present) {
setTXPower(fields.tx_pwr_lvl);
}
if (fields->svc_data_uuid16 != NULL) {
if (fields.svc_data_uuid16 != NULL ||
fields.svc_data_uuid32 != NULL ||
fields.svc_data_uuid128 != NULL)
{
ble_hs_adv_field *field;
uint8_t *data = payload;
while(length > 1) {
field = (ble_hs_adv_field*)data;
u8p = fields->svc_data_uuid16;
length = fields->svc_data_uuid16_len;
if (length < 2) {
NIMBLE_LOGE(LOG_TAG,"Length too small for ESP_BLE_AD_TYPE_SERVICE_DATA");
}
else{
uint16_t uuid = *(uint16_t*)u8p;
setServiceDataUUID(NimBLEUUID(uuid));
if (length > 2) {
setServiceData(std::string(reinterpret_cast<char*>(u8p + 2), length - 2));
if(field->length > length) {
break;
}
if(field->type == BLE_HS_ADV_TYPE_SVC_DATA_UUID16) {
if(field->length > 2) {
uint16_t uuid;
memcpy(&uuid, field->value, 2);
setServiceData(NimBLEUUID(uuid), std::string(reinterpret_cast<char*>(field->value + 2), field->length - 3));
}
}
if(field->type == BLE_HS_ADV_TYPE_SVC_DATA_UUID32) {
if(field->length > 4) {
uint32_t uuid;
memcpy(&uuid, field->value, 4);
setServiceData(NimBLEUUID(uuid), std::string(reinterpret_cast<char*>(field->value + 4), field->length - 5));
}
}
if(field->type == BLE_HS_ADV_TYPE_SVC_DATA_UUID128) {
if(field->length > 16) {
NimBLEUUID uuid(field->value, (size_t)16, false);
setServiceData(uuid, std::string(reinterpret_cast<char*>(field->value + 16), field->length - 17));
}
}
length -= 1 + field->length;
data += 1 + field->length;
}
}
if (fields->svc_data_uuid32 != NULL) {
u8p = fields->svc_data_uuid16;
length = fields->svc_data_uuid16_len;
if (length < 4) {
NIMBLE_LOGE(LOG_TAG,"Length too small for ESP_BLE_AD_TYPE_32SERVICE_DATA");
}
uint32_t uuid = *(uint32_t*) u8p;
setServiceDataUUID(NimBLEUUID(uuid));
if (length > 4) {
setServiceData(std::string(reinterpret_cast<char*>(u8p + 4), length - 4));
}
if (fields.appearance_is_present) {
setAppearance(fields.appearance);
}
if (fields->svc_data_uuid128 != NULL) {
u8p = fields->svc_data_uuid16;
length = fields->svc_data_uuid16_len;
if (length < 16) {
NIMBLE_LOGE(LOG_TAG,"Length too small for ESP_BLE_AD_TYPE_128SERVICE_DATA");
}
setServiceDataUUID(NimBLEUUID(u8p, (size_t)16, false));
if (length > 16) {
setServiceData(std::string(reinterpret_cast<char*>(u8p + 16), length - 16));
}
if (fields.mfg_data != NULL) {
setManufacturerData(std::string(reinterpret_cast<char*>(fields.mfg_data), fields.mfg_data_len));
}
if (fields->appearance_is_present) {
NIMBLE_LOGD(LOG_TAG, " appearance=0x%04x", fields->appearance);
setAppearance(fields->appearance);
}
/**** TODO: create storage and fucntions for these parameters
if (fields->public_tgt_addr != NULL) {
/* TODO: create storage and fucntions for these parameters
if (fields.public_tgt_addr != NULL) {
NIMBLE_LOGD(LOG_TAG, " public_tgt_addr=");
u8p = fields->public_tgt_addr;
for (i = 0; i < fields->num_public_tgt_addrs; i++) {
u8p = fields.public_tgt_addr;
for (i = 0; i < fields.num_public_tgt_addrs; i++) {
NIMBLE_LOGD(LOG_TAG, "public_tgt_addr=%s ", addr_str(u8p));
u8p += BLE_HS_ADV_PUBLIC_TGT_ADDR_ENTRY_LEN;
}
NIMBLE_LOGD(LOG_TAG, "\n");
}
if (fields->slave_itvl_range != NULL) {
if (fields.slave_itvl_range != NULL) {
NIMBLE_LOGD(LOG_TAG, " slave_itvl_range=");
print_bytes(fields->slave_itvl_range, BLE_HS_ADV_SLAVE_ITVL_RANGE_LEN);
print_bytes(fields.slave_itvl_range, BLE_HS_ADV_SLAVE_ITVL_RANGE_LEN);
NIMBLE_LOGD(LOG_TAG, "\n");
}
if (fields->adv_itvl_is_present) {
NIMBLE_LOGD(LOG_TAG, " adv_itvl=0x%04x\n", fields->adv_itvl);
if (fields.adv_itvl_is_present) {
NIMBLE_LOGD(LOG_TAG, " adv_itvl=0x%04x\n", fields.adv_itvl);
}
if (fields->uri != NULL) {
if (fields.uri != NULL) {
NIMBLE_LOGD(LOG_TAG, " uri=");
print_bytes(fields->uri, fields->uri_len);
print_bytes(fields.uri, fields.uri_len);
NIMBLE_LOGD(LOG_TAG, "\n");
}
*/
if (fields->mfg_data != NULL) {
setManufacturerData(std::string(reinterpret_cast<char*>(fields->mfg_data), fields->mfg_data_len));
}
} //parseAdvertisement
@ -393,7 +442,6 @@ void NimBLEAdvertisedDevice::setAdvType(uint8_t advType) {
void NimBLEAdvertisedDevice::setAppearance(uint16_t appearance) {
m_appearance = appearance;
m_haveAppearance = true;
NIMBLE_LOGD(LOG_TAG,"- appearance: %d", m_appearance);
} // setAppearance
@ -404,10 +452,6 @@ void NimBLEAdvertisedDevice::setAppearance(uint16_t appearance) {
void NimBLEAdvertisedDevice::setManufacturerData(std::string manufacturerData) {
m_manufacturerData = manufacturerData;
m_haveManufacturerData = true;
char* pHex = NimBLEUtils::buildHexData(nullptr, (uint8_t*) m_manufacturerData.data(), (uint8_t) m_manufacturerData.length());
NIMBLE_LOGD(LOG_TAG,"- manufacturer data: %s", pHex);
free(pHex);
} // setManufacturerData
@ -418,7 +462,6 @@ void NimBLEAdvertisedDevice::setManufacturerData(std::string manufacturerData) {
void NimBLEAdvertisedDevice::setName(std::string name) {
m_name = name;
m_haveName = true;
NIMBLE_LOGD(LOG_TAG,"- setName(): name: %s", m_name.c_str());
} // setName
@ -429,19 +472,9 @@ void NimBLEAdvertisedDevice::setName(std::string name) {
void NimBLEAdvertisedDevice::setRSSI(int rssi) {
m_rssi = rssi;
m_haveRSSI = true;
NIMBLE_LOGD(LOG_TAG,"- setRSSI(): rssi: %d", m_rssi);
} // setRSSI
/**
* @brief Set the Scan that created this advertised device.
* @param pScan The Scan that created this advertised device.
*/
void NimBLEAdvertisedDevice::setScan(NimBLEScan* pScan) {
m_pScan = pScan;
} // setScan
/**
* @brief Set the Service UUID for this device.
* @param [in] serviceUUID The discovered serviceUUID
@ -459,36 +492,32 @@ void NimBLEAdvertisedDevice::setServiceUUID(const char* serviceUUID) {
void NimBLEAdvertisedDevice::setServiceUUID(NimBLEUUID serviceUUID) {
// Don't add duplicates
for (int i = 0; i < m_serviceUUIDs.size(); i++) {
if (m_serviceUUIDs[i].equals(serviceUUID)) {
if (m_serviceUUIDs[i] == serviceUUID) {
return;
}
}
m_serviceUUIDs.push_back(serviceUUID);
m_haveServiceUUID = true;
NIMBLE_LOGD(LOG_TAG,"- addServiceUUID(): serviceUUID: %s", serviceUUID.toString().c_str());
} // setServiceUUID
/**
* @brief Set the ServiceData value.
* @param [in] data ServiceData value.
* @param [in] uuid The UUID that the service data belongs to.
* @param [in] data The service data.
*/
void NimBLEAdvertisedDevice::setServiceData(std::string serviceData) {
m_haveServiceData = true; // Set the flag that indicates we have service data.
m_serviceData = serviceData; // Save the service data that we received.
void NimBLEAdvertisedDevice::setServiceData(NimBLEUUID uuid, std::string data) {
m_haveServiceData = true;
for(auto &it : m_serviceDataVec) {
if(it.first == uuid) {
it.second = data;
return;
}
}
m_serviceDataVec.push_back({uuid, data});
} //setServiceData
/**
* @brief Set the ServiceDataUUID value.
* @param [in] data ServiceDataUUID value.
*/
void NimBLEAdvertisedDevice::setServiceDataUUID(NimBLEUUID uuid) {
m_haveServiceData = true; // Set the flag that indicates we have service data.
m_serviceDataUUID = uuid;
} // setServiceDataUUID
/**
* @brief Set the power level for this device.
* @param [in] txPower The discovered power level.
@ -496,7 +525,6 @@ void NimBLEAdvertisedDevice::setServiceDataUUID(NimBLEUUID uuid) {
void NimBLEAdvertisedDevice::setTXPower(int8_t txPower) {
m_txPower = txPower;
m_haveTXPower = true;
NIMBLE_LOGD(LOG_TAG,"- txPower: %d", m_txPower);
} // setTXPower
@ -532,43 +560,60 @@ std::string NimBLEAdvertisedDevice::toString() {
res += val;
}
res += ", advType: " + std::string(NimBLEUtils::advTypeToString(m_advType));
if(haveServiceData()) {
size_t count = getServiceDataCount();
res += "\nService Data:";
for(size_t i = 0; i < count; i++) {
res += "\nUUID: " + std::string(getServiceDataUUID(i));
res += ", Data: " + getServiceData(i);
}
}
return res;
} // toString
/**
* @brief Get the payload advertised by the device.
* @return The advertisement payload.
*/
uint8_t* NimBLEAdvertisedDevice::getPayload() {
return m_payload;
}
} // getPayload
/**
* @brief Get the advertised device address type.
* @return The device address type:
* * BLE_ADDR_PUBLIC (0x00)
* * BLE_ADDR_RANDOM (0x01)
* * BLE_ADDR_PUBLIC_ID (0x02)
* * BLE_ADDR_RANDOM_ID (0x03)
*/
uint8_t NimBLEAdvertisedDevice::getAddressType() {
return m_addressType;
}
return m_address.getType();
} // getAddressType
/**
* @brief Get the timeStamp of when the device last advertised.
* @return The timeStamp of when the device was last seen.
*/
time_t NimBLEAdvertisedDevice::getTimestamp() {
return m_timestamp;
}
void NimBLEAdvertisedDevice::setAddressType(uint8_t type) {
m_addressType = type;
}
} // getTimestamp
/**
* @brief Get the length of the payload advertised by the device.
* @return The size of the payload in bytes.
*/
size_t NimBLEAdvertisedDevice::getPayloadLength() {
return m_payloadLength;
}
} // getPayloadLength
void NimBLEAdvertisedDevice::setAdvertisementResult(uint8_t* payload, uint8_t length){
m_payload = payload;
m_payloadLength = length;
}
#endif // #if defined( CONFIG_BT_NIMBLE_ROLE_CENTRAL)
#endif /* CONFIG_BT_ENABLED */

View File

@ -46,8 +46,16 @@ public:
uint16_t getAppearance();
std::string getManufacturerData();
/**
* @brief A template to convert the service data to <type\>.
* @tparam T The type to convert the data to.
* @param [in] skipSizeCheck If true it will skip checking if the data size is less than <tt>sizeof(<type\>)</tt>.
* @return The data converted to <type\> or NULL if skipSizeCheck is false and the data is
* less than <tt>sizeof(<type\>)</tt>.
* @details <b>Use:</b> <tt>getManufacturerData<type>(skipSizeCheck);</tt>
*/
template<typename T>
T getManufacturerData(bool skipSizeCheck = false) {
T getManufacturerData(bool skipSizeCheck = false) {
std::string data = getManufacturerData();
if(!skipSizeCheck && data.size() < sizeof(T)) return T();
const char *pData = data.data();
@ -57,51 +65,73 @@ public:
std::string getName();
int getRSSI();
NimBLEScan* getScan();
std::string getServiceData();
size_t getServiceDataCount();
std::string getServiceData(uint8_t index = 0);
std::string getServiceData(const NimBLEUUID &uuid) const;
/**
* @brief A template to convert the service data to <tt><type\></tt>.
* @tparam T The type to convert the data to.
* @param [in] index The vector index of the service data requested.
* @param [in] skipSizeCheck If true it will skip checking if the data size is less than <tt>sizeof(<type\>)</tt>.
* @return The data converted to <type\> or NULL if skipSizeCheck is false and the data is
* less than <tt>sizeof(<type\>)</tt>.
* @details <b>Use:</b> <tt>getServiceData<type>(skipSizeCheck);</tt>
*/
template<typename T>
T getServiceData(bool skipSizeCheck = false) {
std::string data = getServiceData();
T getServiceData(uint8_t index = 0, bool skipSizeCheck = false) {
std::string data = getServiceData(index);
if(!skipSizeCheck && data.size() < sizeof(T)) return T();
const char *pData = data.data();
return *((T *)pData);
}
NimBLEUUID getServiceDataUUID();
NimBLEUUID getServiceUUID();
/**
* @brief A template to convert the service data to <tt><type\></tt>.
* @tparam T The type to convert the data to.
* @param [in] uuid The uuid of the service data requested.
* @param [in] skipSizeCheck If true it will skip checking if the data size is less than <tt>sizeof(<type\>)</tt>.
* @return The data converted to <type\> or NULL if skipSizeCheck is false and the data is
* less than <tt>sizeof(<type\>)</tt>.
* @details <b>Use:</b> <tt>getServiceData<type>(skipSizeCheck);</tt>
*/
template<typename T>
T getServiceData(const NimBLEUUID &uuid, bool skipSizeCheck = false) {
std::string data = getServiceData(uuid);
if(!skipSizeCheck && data.size() < sizeof(T)) return T();
const char *pData = data.data();
return *((T *)pData);
}
NimBLEUUID getServiceDataUUID(uint8_t index = 0);
NimBLEUUID getServiceUUID(uint8_t index = 0);
size_t getServiceUUIDCount();
int8_t getTXPower();
uint8_t* getPayload();
size_t getPayloadLength();
uint8_t getAddressType();
time_t getTimestamp();
void setAddressType(uint8_t type);
bool isAdvertisingService(const NimBLEUUID &uuid);
bool haveAppearance();
bool haveManufacturerData();
bool haveName();
bool haveRSSI();
bool haveServiceData();
bool haveServiceUUID();
bool haveTXPower();
std::string toString();
bool isAdvertisingService(const NimBLEUUID &uuid) const;
bool haveAppearance();
bool haveManufacturerData();
bool haveName();
bool haveRSSI();
bool haveServiceData();
bool haveServiceUUID();
bool haveTXPower();
std::string toString();
private:
friend class NimBLEScan;
void parseAdvertisement(ble_hs_adv_fields *fields);
void parseAdvertisement(uint8_t* payload, uint8_t length);
void setAddress(NimBLEAddress address);
void setAdvType(uint8_t advType);
void setAdvertisementResult(uint8_t* payload, uint8_t length);
void setAppearance(uint16_t appearance);
void setManufacturerData(std::string manufacturerData);
void setName(std::string name);
void setRSSI(int rssi);
void setScan(NimBLEScan* pScan);
void setServiceData(std::string data);
void setServiceDataUUID(NimBLEUUID uuid);
void setServiceData(NimBLEUUID serviceUUID, std::string data);
void setServiceUUID(const char* serviceUUID);
void setServiceUUID(NimBLEUUID serviceUUID);
void setTXPower(int8_t txPower);
@ -118,20 +148,17 @@ private:
NimBLEAddress m_address = NimBLEAddress("");
uint8_t m_advType;
uint16_t m_appearance;
int m_deviceType;
std::string m_manufacturerData;
std::string m_name;
NimBLEScan* m_pScan;
int m_rssi;
std::vector<NimBLEUUID> m_serviceUUIDs;
int8_t m_txPower;
std::string m_serviceData;
NimBLEUUID m_serviceDataUUID;
uint8_t* m_payload;
size_t m_payloadLength;
uint8_t m_addressType;
time_t m_timestamp;
bool m_callbackSent;
std::vector<NimBLEUUID> m_serviceUUIDs;
std::vector<std::pair<NimBLEUUID, std::string>>m_serviceDataVec;
};
/**
@ -150,7 +177,6 @@ public:
* As we are scanning, we will find new devices. When found, this call back is invoked with a reference to the
* device that was found. During any individual scan, a device will only be detected one time.
*/
//virtual void onResult(NimBLEAdvertisedDevice advertisedDevice) = 0;
virtual void onResult(NimBLEAdvertisedDevice* advertisedDevice) = 0;
};

View File

@ -31,7 +31,6 @@ static const char* LOG_TAG = "NimBLEAdvertising";
/**
* @brief Construct a default advertising object.
*
*/
NimBLEAdvertising::NimBLEAdvertising() {
memset(&m_advData, 0, sizeof m_advData);
@ -55,6 +54,11 @@ NimBLEAdvertising::NimBLEAdvertising() {
m_advParams.itvl_min = 0;
m_advParams.itvl_max = 0;
m_customAdvData = false;
m_customScanResponseData = false;
m_scanResp = true;
m_advDataSet = false;
} // NimBLEAdvertising
@ -64,6 +68,7 @@ NimBLEAdvertising::NimBLEAdvertising() {
*/
void NimBLEAdvertising::addServiceUUID(const NimBLEUUID &serviceUUID) {
m_serviceUUIDs.push_back(serviceUUID);
m_advDataSet = false;
} // addServiceUUID
@ -76,45 +81,74 @@ void NimBLEAdvertising::addServiceUUID(const char* serviceUUID) {
} // addServiceUUID
/**
* @brief Add a service uuid to exposed list of services.
* @param [in] serviceUUID The UUID of the service to expose.
*/
void NimBLEAdvertising::removeServiceUUID(const NimBLEUUID &serviceUUID) {
//m_serviceUUIDs.erase(std::remove_if(m_serviceUUIDs.begin(), m_serviceUUIDs.end(),[serviceUUID](const NimBLEUUID &s) {return serviceUUID == s;}), m_serviceUUIDs.end());
for(auto it = m_serviceUUIDs.begin(); it != m_serviceUUIDs.end(); ++it) {
if((*it) == serviceUUID) {
m_serviceUUIDs.erase(it);
break;
}
}
m_advDataSet = false;
} // addServiceUUID
/**
* @brief Set the device appearance in the advertising data.
* The appearance attribute is of type 0x19. The codes for distinct appearances can be found here:
* The codes for distinct appearances can be found here:\n
* https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.gap.appearance.xml.
* @param [in] appearance The appearance of the device in the advertising data.
* @return N/A.
*/
void NimBLEAdvertising::setAppearance(uint16_t appearance) {
m_advData.appearance = appearance;
m_advData.appearance_is_present = 1;
} // setAppearance
/**
* @brief Set the type of advertisment to use.
* @param [in] adv_type:
* * BLE_HCI_ADV_TYPE_ADV_IND (0) - indirect advertising
* * BLE_HCI_ADV_TYPE_ADV_DIRECT_IND_HD (1) - direct advertisng - high duty cycle
* * BLE_HCI_ADV_TYPE_ADV_SCAN_IND (2) - indirect scan response
* * BLE_HCI_ADV_TYPE_ADV_NONCONN_IND (3) - indirect advertisng - not connectable
* * BLE_HCI_ADV_TYPE_ADV_DIRECT_IND_LD (4) - direct advertising - low duty cycle
*/
void NimBLEAdvertising::setAdvertisementType(uint8_t adv_type){
m_advParams.conn_mode = adv_type;
} // setAdvertisementType
/**
* @brief Set the minimum advertising interval.
* @param [in] mininterval Minimum value for advertising interval in 0.625ms units, 0 = use default.
*/
void NimBLEAdvertising::setMinInterval(uint16_t mininterval) {
m_advParams.itvl_min = mininterval;
} // setMinInterval
/**
* @brief Set the maximum advertising interval.
* @param [in] maxinterval Maximum value for advertising interval in 0.625ms units, 0 = use default.
*/
void NimBLEAdvertising::setMaxInterval(uint16_t maxinterval) {
m_advParams.itvl_max = maxinterval;
} // setMaxInterval
/* These are dummy functions for now for compatibility */
void NimBLEAdvertising::setMinPreferred(uint16_t mininterval) {
//m_advData.min_interval = mininterval;
} //
void NimBLEAdvertising::setMaxPreferred(uint16_t maxinterval) {
//m_advData.max_interval = maxinterval;
} //
/*******************************************************/
/**
* @brief Set if scan response is available.
* @param [in] set true = scan response available.
*/
void NimBLEAdvertising::setScanResponse(bool set) {
m_scanResp = set;
}
} // setScanResponse
/**
* @brief Set the filtering for the scan filter.
@ -145,9 +179,13 @@ void NimBLEAdvertising::setScanFilter(bool scanRequestWhitelistOnly, bool connec
}
} // setScanFilter
/**
* @brief Set the advertisement data that is to be published in a regular advertisement.
* @param [in] advertisementData The data to be advertised.
* @details The use of this function will replace any data set with addServiceUUID\n
* or setAppearance. If you wish for these to be advertised you must include them\n
* in the advertisementData parameter sent.
*/
void NimBLEAdvertising::setAdvertisementData(NimBLEAdvertisementData& advertisementData) {
@ -166,6 +204,8 @@ void NimBLEAdvertising::setAdvertisementData(NimBLEAdvertisementData& advertisem
/**
* @brief Set the advertisement data that is to be published in a scan response.
* @param [in] advertisementData The data to be advertised.
* @details Calling this without also using setAdvertisementData will have no effect.\n
* When using custom scan response data you must also use custom advertisement data.
*/
void NimBLEAdvertising::setScanResponseData(NimBLEAdvertisementData& advertisementData) {
NIMBLE_LOGD(LOG_TAG, ">> setScanResponseData");
@ -182,10 +222,10 @@ void NimBLEAdvertising::setScanResponseData(NimBLEAdvertisementData& advertiseme
/**
* @brief Start advertising.
* Start advertising.
* @return N/A.
* @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.
*/
void NimBLEAdvertising::start() {
void NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdvertising *pAdv)) {
NIMBLE_LOGD(LOG_TAG, ">> Advertising start: customAdvData: %d, customScanResponseData: %d", m_customAdvData, m_customScanResponseData);
// If Host is not synced we cannot start advertising.
@ -211,6 +251,15 @@ void NimBLEAdvertising::start() {
return;
}
if(duration == 0){
duration = BLE_HS_FOREVER;
}
else{
duration = duration*1000; // convert duration to milliseconds
}
m_advCompCB = advCompleteCB;
int rc = 0;
if (!m_customAdvData && !m_advDataSet) {
@ -356,13 +405,13 @@ void NimBLEAdvertising::start() {
}
#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
rc = ble_gap_adv_start(0, NULL, BLE_HS_FOREVER,
rc = ble_gap_adv_start(0, NULL, duration,
&m_advParams,
(pServer != nullptr) ? NimBLEServer::handleGapEvent : NULL,
pServer);
(pServer != nullptr) ? NimBLEServer::handleGapEvent : NimBLEAdvertising::handleGapEvent,
(pServer != nullptr) ? (void*)pServer : (void*)this);
#else
rc = ble_gap_adv_start(0, NULL, BLE_HS_FOREVER,
&m_advParams, NULL,NULL);
rc = ble_gap_adv_start(0, NULL, duration,
&m_advParams, NimBLEAdvertising::handleGapEvent, this);
#endif
if (rc != 0) {
NIMBLE_LOGC(LOG_TAG, "Error enabling advertising; rc=%d, %s", rc, NimBLEUtils::returnCodeToString(rc));
@ -375,8 +424,6 @@ void NimBLEAdvertising::start() {
/**
* @brief Stop advertising.
* Stop advertising.
* @return N/A.
*/
void NimBLEAdvertising::stop() {
NIMBLE_LOGD(LOG_TAG, ">> stop");
@ -391,6 +438,25 @@ void NimBLEAdvertising::stop() {
/**
* @brief Handles the callback when advertising stops.
*/
void NimBLEAdvertising::advCompleteCB() {
if(m_advCompCB != nullptr) {
m_advCompCB(this);
}
}
/**
* @brief Check if currently advertising.
* @return true if advertising is active.
*/
bool NimBLEAdvertising::isAdvertising() {
return ble_gap_adv_active();
}
/*
* Host reset seems to clear advertising data,
* we need clear the flag so it reloads it.
*/
@ -399,6 +465,22 @@ void NimBLEAdvertising::onHostReset() {
}
/**
* @brief Handler for gap events when not using peripheral role.
* @param [in] event the event data.
* @param [in] arg pointer to the advertising instance.
*/
/*STATIC*/
int NimBLEAdvertising::handleGapEvent(struct ble_gap_event *event, void *arg) {
NimBLEAdvertising *pAdv = (NimBLEAdvertising*)arg;
if(event->type == BLE_GAP_EVENT_ADV_COMPLETE) {
pAdv->advCompleteCB();
}
return 0;
}
/**
* @brief Add data to the payload to be advertised.
* @param [in] data The data to be added to the payload.
@ -411,6 +493,19 @@ void NimBLEAdvertisementData::addData(const std::string &data) {
} // addData
/**
* @brief Add data to the payload to be advertised.
* @param [in] data The data to be added to the payload.
* @param [in] length The size of data to be added to the payload.
*/
void NimBLEAdvertisementData::addData(char * data, size_t length){
if ((m_payload.length() + length) > BLE_HS_ADV_MAX_SZ) {
return;
}
m_payload.append(data,length);
} // addData
/**
* @brief Set the appearance.
* @param [in] appearance The appearance code value.
@ -427,8 +522,8 @@ void NimBLEAdvertisementData::setAppearance(uint16_t appearance) {
/**
* @brief Set the complete services.
* @param [in] uuid The single service to advertise.
* @brief Set the complete services to advertise.
* @param [in] uuid The UUID of the service.
*/
void NimBLEAdvertisementData::setCompleteServices(const NimBLEUUID &uuid) {
char cdata[2];
@ -465,16 +560,7 @@ void NimBLEAdvertisementData::setCompleteServices(const NimBLEUUID &uuid) {
/**
* @brief Set the advertisement flags.
* @param [in] The flags to be set in the advertisement.
* * ****DO NOT USE THESE****
* * ESP_BLE_ADV_FLAG_LIMIT_DISC
* * ESP_BLE_ADV_FLAG_GEN_DISC
* * ESP_BLE_ADV_FLAG_BREDR_NOT_SPT
* * ESP_BLE_ADV_FLAG_DMT_CONTROLLER_SPT
* * ESP_BLE_ADV_FLAG_DMT_HOST_SPT
* * ESP_BLE_ADV_FLAG_NON_LIMIT_DISC
* *
* * ****THESE ARE SUPPORTED****
* @param [in] flag The flags to be set in the advertisement.
* * BLE_HS_ADV_F_DISC_LTD
* * BLE_HS_ADV_F_DISC_GEN
* * BLE_HS_ADV_F_BREDR_UNSUP - must always use with NimBLE
@ -490,7 +576,7 @@ void NimBLEAdvertisementData::setFlags(uint8_t flag) {
/**
* @brief Set manufacturer specific data.
* @param [in] data Manufacturer data.
* @param [in] data The manufacturer data to advertise.
*/
void NimBLEAdvertisementData::setManufacturerData(const std::string &data) {
NIMBLE_LOGD("NimBLEAdvertisementData", ">> setManufacturerData");
@ -503,8 +589,8 @@ void NimBLEAdvertisementData::setManufacturerData(const std::string &data) {
/**
* @brief Set the name.
* @param [in] The complete name of the device.
* @brief Set the complete name of this device.
* @param [in] name The name to advertise.
*/
void NimBLEAdvertisementData::setName(const std::string &name) {
NIMBLE_LOGD("NimBLEAdvertisementData", ">> setName: %s", name.c_str());
@ -517,7 +603,7 @@ void NimBLEAdvertisementData::setName(const std::string &name) {
/**
* @brief Set the partial services.
* @brief Set the partial services to advertise.
* @param [in] uuid The single service to advertise.
*/
void NimBLEAdvertisementData::setPartialServices(const NimBLEUUID &uuid) {
@ -555,8 +641,8 @@ void NimBLEAdvertisementData::setPartialServices(const NimBLEUUID &uuid) {
/**
* @brief Set the service data (UUID + data)
* @param [in] uuid The UUID to set with the service data. Size of UUID will be used.
* @param [in] data The data to be associated with the service data advert.
* @param [in] uuid The UUID to set with the service data.
* @param [in] data The data to be associated with the service data advertised.
*/
void NimBLEAdvertisementData::setServiceData(const NimBLEUUID &uuid, const std::string &data) {
char cdata[2];
@ -593,7 +679,7 @@ void NimBLEAdvertisementData::setServiceData(const NimBLEUUID &uuid, const std::
/**
* @brief Set the short name.
* @param [in] The short name of the device.
* @param [in] name The short name of the device.
*/
void NimBLEAdvertisementData::setShortName(const std::string &name) {
NIMBLE_LOGD("NimBLEAdvertisementData", ">> setShortName: %s", name.c_str());

View File

@ -57,6 +57,7 @@ public:
void setServiceData(const NimBLEUUID &uuid, const std::string &data);
void setShortName(const std::string &name);
void addData(const std::string &data); // Add data to the payload.
void addData(char * data, size_t length);
std::string getPayload(); // Retrieve the current advert payload.
private:
@ -75,34 +76,35 @@ public:
NimBLEAdvertising();
void addServiceUUID(const NimBLEUUID &serviceUUID);
void addServiceUUID(const char* serviceUUID);
void start();
void removeServiceUUID(const NimBLEUUID &serviceUUID);
void start(uint32_t duration = 0, void (*advCompleteCB)(NimBLEAdvertising *pAdv) = nullptr);
void stop();
void setAppearance(uint16_t appearance);
void setAdvertisementType(uint8_t adv_type);
void setMaxInterval(uint16_t maxinterval);
void setMinInterval(uint16_t mininterval);
void setAdvertisementData(NimBLEAdvertisementData& advertisementData);
void setScanFilter(bool scanRequertWhitelistOnly, bool connectWhitelistOnly);
void setScanFilter(bool scanRequestWhitelistOnly, bool connectWhitelistOnly);
void setScanResponseData(NimBLEAdvertisementData& advertisementData);
void setPrivateAddress(uint8_t type = BLE_ADDR_RANDOM);
void setMinPreferred(uint16_t);
void setMaxPreferred(uint16_t);
void setScanResponse(bool);
void advCompleteCB();
bool isAdvertising();
private:
friend class NimBLEDevice;
void onHostReset();
void onHostReset();
static int handleGapEvent(struct ble_gap_event *event, void *arg);
ble_hs_adv_fields m_advData;
ble_hs_adv_fields m_scanData;
ble_gap_adv_params m_advParams;
ble_hs_adv_fields m_advData;
ble_hs_adv_fields m_scanData;
ble_gap_adv_params m_advParams;
std::vector<NimBLEUUID> m_serviceUUIDs;
bool m_customAdvData = false; // Are we using custom advertising data?
bool m_customScanResponseData = false; // Are we using custom scan response data?
bool m_scanResp = true;
bool m_advDataSet = false;
bool m_customAdvData;
bool m_customScanResponseData;
bool m_scanResp;
bool m_advDataSet;
void (*m_advCompCB)(NimBLEAdvertising *pAdv);
};

View File

@ -15,6 +15,7 @@
#if defined(CONFIG_BT_ENABLED)
#include <string.h>
#include <algorithm>
#include "NimBLEBeacon.h"
#include "NimBLELog.h"
@ -22,6 +23,10 @@
static const char* LOG_TAG = "NimBLEBeacon";
/**
* @brief Construct a default beacon object.
*/
NimBLEBeacon::NimBLEBeacon() {
m_beaconData.manufacturerId = 0x4c00;
m_beaconData.subType = 0x02;
@ -32,32 +37,64 @@ NimBLEBeacon::NimBLEBeacon() {
memset(m_beaconData.proximityUUID, 0, sizeof(m_beaconData.proximityUUID));
} // NimBLEBeacon
/**
* @brief Retrieve the data that is being advertised.
* @return The advertised data.
*/
std::string NimBLEBeacon::getData() {
return std::string((char*) &m_beaconData, sizeof(m_beaconData));
} // getData
/**
* @brief Get the major value being advertised.
* @return The major value advertised.
*/
uint16_t NimBLEBeacon::getMajor() {
return m_beaconData.major;
}
/**
* @brief Get the manufacturer ID being advertised.
* @return The manufacturer ID value advertised.
*/
uint16_t NimBLEBeacon::getManufacturerId() {
return m_beaconData.manufacturerId;
}
/**
* @brief Get the minor value being advertised.
* @return minor value advertised.
*/
uint16_t NimBLEBeacon::getMinor() {
return m_beaconData.minor;
}
/**
* @brief Get the proximity UUID being advertised.
* @return The UUID advertised.
*/
NimBLEUUID NimBLEBeacon::getProximityUUID() {
return NimBLEUUID(m_beaconData.proximityUUID, 16, false);
return NimBLEUUID(m_beaconData.proximityUUID, 16, true);
}
/**
* @brief Get the signal power being advertised.
* @return signal power level advertised.
*/
int8_t NimBLEBeacon::getSignalPower() {
return m_beaconData.signalPower;
}
/**
* Set the raw data for the beacon record.
* @brief Set the raw data for the beacon record.
* @param [in] data The raw beacon data.
*/
void NimBLEBeacon::setData(const std::string &data) {
if (data.length() != sizeof(m_beaconData)) {
@ -68,24 +105,51 @@ void NimBLEBeacon::setData(const std::string &data) {
memcpy(&m_beaconData, data.data(), sizeof(m_beaconData));
} // setData
/**
* @brief Set the major value.
* @param [in] major The major value.
*/
void NimBLEBeacon::setMajor(uint16_t major) {
m_beaconData.major = ENDIAN_CHANGE_U16(major);
} // setMajor
/**
* @brief Set the manufacturer ID.
* @param [in] manufacturerId The manufacturer ID value.
*/
void NimBLEBeacon::setManufacturerId(uint16_t manufacturerId) {
m_beaconData.manufacturerId = ENDIAN_CHANGE_U16(manufacturerId);
} // setManufacturerId
/**
* @brief Set the minor value.
* @param [in] minor The minor value.
*/
void NimBLEBeacon::setMinor(uint16_t minor) {
m_beaconData.minor = ENDIAN_CHANGE_U16(minor);
} // setMinior
/**
* @brief Set the proximity UUID.
* @param [in] uuid The proximity UUID.
*/
void NimBLEBeacon::setProximityUUID(const NimBLEUUID &uuid) {
NimBLEUUID temp_uuid = uuid;
temp_uuid.to128();
memcpy(m_beaconData.proximityUUID, temp_uuid.getNative()->u128.value, 16);
std::reverse_copy(temp_uuid.getNative()->u128.value,
temp_uuid.getNative()->u128.value + 16,
m_beaconData.proximityUUID);
} // setProximityUUID
/**
* @brief Set the signal power.
* @param [in] signalPower The signal power value.
*/
void NimBLEBeacon::setSignalPower(int8_t signalPower) {
m_beaconData.signalPower = signalPower;
} // setSignalPower

View File

@ -16,16 +16,18 @@
#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#include "NimBLECharacteristic.h"
#include "NimBLE2902.h"
#include "NimBLE2904.h"
#include "NimBLEDevice.h"
#include "NimBLELog.h"
#define NULL_HANDLE (0xffff)
#define NIMBLE_SUB_NOTIFY 0x0001
#define NIMBLE_SUB_INDICATE 0x0002
static NimBLECharacteristicCallbacks defaultCallback;
static const char* LOG_TAG = "NimBLECharacteristic";
/**
* @brief Construct a characteristic
* @param [in] uuid - UUID (const char*) for the characteristic.
@ -58,6 +60,9 @@ NimBLECharacteristic::NimBLECharacteristic(const NimBLEUUID &uuid, uint16_t prop
* @brief Destructor.
*/
NimBLECharacteristic::~NimBLECharacteristic() {
for(auto &it : m_dscVec) {
delete it;
}
} // ~NimBLECharacteristic
@ -65,6 +70,7 @@ NimBLECharacteristic::~NimBLECharacteristic() {
* @brief Create a new BLE Descriptor associated with this characteristic.
* @param [in] uuid - The UUID of the descriptor.
* @param [in] properties - The properties of the descriptor.
* @param [in] max_len - The max length in bytes of the descriptor value.
* @return The new BLE descriptor.
*/
NimBLEDescriptor* NimBLECharacteristic::createDescriptor(const char* uuid, uint32_t properties, uint16_t max_len) {
@ -76,25 +82,15 @@ NimBLEDescriptor* NimBLECharacteristic::createDescriptor(const char* uuid, uint3
* @brief Create a new BLE Descriptor associated with this characteristic.
* @param [in] uuid - The UUID of the descriptor.
* @param [in] properties - The properties of the descriptor.
* @param [in] max_len - The max length in bytes of the descriptor value.
* @return The new BLE descriptor.
*/
NimBLEDescriptor* NimBLECharacteristic::createDescriptor(const NimBLEUUID &uuid, uint32_t properties, uint16_t max_len) {
NimBLEDescriptor* pDescriptor = nullptr;
if(uuid == NimBLEUUID(uint16_t(0x2902))) {
if(!(m_properties & BLE_GATT_CHR_F_NOTIFY) && !(m_properties & BLE_GATT_CHR_F_INDICATE)) {
assert(0 && "Cannot create 2902 descriptior without characteristic notification or indication property set");
}
// We cannot have more than one 2902 descriptor, if it's already been created just return a pointer to it.
pDescriptor = getDescriptorByUUID(uuid);
if(pDescriptor == nullptr) {
pDescriptor = new NimBLE2902(this);
} else {
return pDescriptor;
}
assert(0 && "0x2902 descriptors cannot be manually created");
} else if (uuid == NimBLEUUID(uint16_t(0x2904))) {
pDescriptor = new NimBLE2904(this);
} else {
pDescriptor = new NimBLEDescriptor(uuid, properties, max_len, this);
}
@ -106,8 +102,8 @@ NimBLEDescriptor* NimBLECharacteristic::createDescriptor(const NimBLEUUID &uuid,
/**
* @brief Return the BLE Descriptor for the given UUID if associated with this characteristic.
* @param [in] descriptorUUID The UUID of the descriptor that we wish to retrieve.
* @return The BLE Descriptor. If no such descriptor is associated with the characteristic, nullptr is returned.
* @param [in] uuid The UUID of the descriptor that we wish to retrieve.
* @return pointer to the NimBLEDescriptor. If no such descriptor is associated with the characteristic, nullptr is returned.
*/
NimBLEDescriptor* NimBLECharacteristic::getDescriptorByUUID(const char* uuid) {
return getDescriptorByUUID(NimBLEUUID(uuid));
@ -116,8 +112,8 @@ NimBLEDescriptor* NimBLECharacteristic::getDescriptorByUUID(const char* uuid) {
/**
* @brief Return the BLE Descriptor for the given UUID if associated with this characteristic.
* @param [in] descriptorUUID The UUID of the descriptor that we wish to retrieve.
* @return The BLE Descriptor. If no such descriptor is associated with the characteristic, nullptr is returned.
* @param [in] uuid The UUID of the descriptor that we wish to retrieve.
* @return pointer to the NimBLEDescriptor. If no such descriptor is associated with the characteristic, nullptr is returned.
*/
NimBLEDescriptor* NimBLECharacteristic::getDescriptorByUUID(const NimBLEUUID &uuid) {
for (auto &it : m_dscVec) {
@ -138,6 +134,10 @@ uint16_t NimBLECharacteristic::getHandle() {
} // getHandle
/**
* @brief Get the properties of the characteristic.
* @return The properties of the characteristic.
*/
uint16_t NimBLECharacteristic::getProperties() {
return m_properties;
} // getProperties
@ -162,7 +162,7 @@ NimBLEUUID NimBLECharacteristic::getUUID() {
/**
* @brief Retrieve the current value of the characteristic.
* @return A pointer to storage containing the current characteristic value.
* @return A std::string containing the current characteristic value.
*/
std::string NimBLECharacteristic::getValue(time_t *timestamp) {
portENTER_CRITICAL(&m_valMux);
@ -189,12 +189,16 @@ size_t NimBLECharacteristic::getDataLength() {
}
/**
* @brief STATIC callback to handle events from the NimBLE stack.
*/
int NimBLECharacteristic::handleGapEvent(uint16_t conn_handle, uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt,
void *arg)
{
const ble_uuid_t *uuid;
int rc;
struct ble_gap_conn_desc desc;
NimBLECharacteristic* pCharacteristic = (NimBLECharacteristic*)arg;
NIMBLE_LOGD(LOG_TAG, "Characteristic %s %s event", pCharacteristic->getUUID().toString().c_str(),
@ -207,7 +211,10 @@ int NimBLECharacteristic::handleGapEvent(uint16_t conn_handle, uint16_t attr_han
// If the packet header is only 8 bytes this is a follow up of a long read
// so we don't want to call the onRead() callback again.
if(ctxt->om->om_pkthdr_len > 8) {
rc = ble_gap_conn_find(conn_handle, &desc);
assert(rc == 0);
pCharacteristic->m_pCallbacks->onRead(pCharacteristic);
pCharacteristic->m_pCallbacks->onRead(pCharacteristic, &desc);
}
portENTER_CRITICAL(&pCharacteristic->m_valMux);
@ -233,14 +240,15 @@ int NimBLECharacteristic::handleGapEvent(uint16_t conn_handle, uint16_t attr_han
if((len + next->om_len) > BLE_ATT_ATTR_MAX_LEN) {
return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
}
memcpy(&buf[len-1], next->om_data, next->om_len);
memcpy(&buf[len], next->om_data, next->om_len);
len += next->om_len;
next = SLIST_NEXT(next, om_next);
}
rc = ble_gap_conn_find(conn_handle, &desc);
assert(rc == 0);
pCharacteristic->setValue(buf, len);
pCharacteristic->m_pCallbacks->onWrite(pCharacteristic);
pCharacteristic->m_pCallbacks->onWrite(pCharacteristic, &desc);
return 0;
}
default:
@ -253,70 +261,70 @@ int NimBLECharacteristic::handleGapEvent(uint16_t conn_handle, uint16_t attr_han
/**
* @brief Set the subscribe status for this characteristic.
* This will maintain a map of subscribed clients and their indicate/notify status.
* @return N/A
* @brief Get the number of clients subscribed to the characteristic.
* @returns Number of clients subscribed to notifications / indications.
*/
size_t NimBLECharacteristic::getSubscribedCount() {
return m_subscribedVec.size();
}
/**
* @brief Set the subscribe status for this characteristic.\n
* This will maintain a vector of subscribed clients and their indicate/notify status.
*/
void NimBLECharacteristic::setSubscribe(struct ble_gap_event *event) {
uint16_t subVal = 0;
if(event->subscribe.cur_notify) {
subVal |= NIMBLE_DESC_FLAG_NOTIFY;
}
if(event->subscribe.cur_indicate) {
subVal |= NIMBLE_DESC_FLAG_INDICATE;
}
if(m_pTaskData != nullptr) {
m_pTaskData->rc = (subVal & NIMBLE_DESC_FLAG_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);
NimBLE2902* p2902 = (NimBLE2902*)getDescriptorByUUID(uint16_t(0x2902));
if(p2902 == nullptr){
ESP_LOGE(LOG_TAG, "No 2902 descriptor found for %s",
std::string(getUUID()).c_str());
ble_gap_conn_desc desc;
if(ble_gap_conn_find(event->subscribe.conn_handle, &desc) != 0) {
return;
}
p2902->setNotifications(subVal & NIMBLE_DESC_FLAG_NOTIFY);
p2902->setIndications(subVal & NIMBLE_DESC_FLAG_INDICATE);
p2902->m_pCallbacks->onWrite(p2902);
uint16_t subVal = 0;
if(event->subscribe.cur_notify > 0 && (m_properties & NIMBLE_PROPERTY::NOTIFY)) {
subVal |= NIMBLE_SUB_NOTIFY;
}
if(event->subscribe.cur_indicate && (m_properties & NIMBLE_PROPERTY::INDICATE)) {
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);
auto it = p2902->m_subscribedVec.begin();
for(;it != p2902->m_subscribedVec.end(); ++it) {
if((*it).conn_id == event->subscribe.conn_handle) {
m_pCallbacks->onSubscribe(this, &desc, subVal);
auto it = m_subscribedVec.begin();
for(;it != m_subscribedVec.end(); ++it) {
if((*it).first == event->subscribe.conn_handle) {
break;
}
}
if(subVal > 0) {
if(it == p2902->m_subscribedVec.end()) {
chr_sub_status_t client_sub;
client_sub.conn_id = event->subscribe.conn_handle;
client_sub.sub_val = subVal;
p2902->m_subscribedVec.push_back(client_sub);
if(it == m_subscribedVec.end()) {
m_subscribedVec.push_back({event->subscribe.conn_handle, subVal});
return;
}
(*it).sub_val = subVal;
(*it).second = subVal;
} else if(it != p2902->m_subscribedVec.end()) {
p2902->m_subscribedVec.erase(it);
p2902->m_subscribedVec.shrink_to_fit();
} else if(it != m_subscribedVec.end()) {
m_subscribedVec.erase(it);
m_subscribedVec.shrink_to_fit();
}
}
/**
* @brief Send an indication.
* An indication is a transmission of up to the first 20 bytes of the characteristic value. An indication
* will block waiting a positive confirmation from the client.
* @return N/A
* @brief Send an indication.\n
* An indication is a transmission of up to the first 20 bytes of the characteristic value.\n
* An indication will block waiting for a positive confirmation from the client.
*/
void NimBLECharacteristic::indicate() {
NIMBLE_LOGD(LOG_TAG, ">> indicate: length: %d", getDataLength());
@ -325,23 +333,24 @@ void NimBLECharacteristic::indicate() {
} // indicate
/**
* @brief Send a notify.
* A notification is a transmission of up to the first 20 bytes of the characteristic value. An notification
* will not block; it is a fire and forget.
* @return N/A.
* @brief Send a notification.\n
* A notification is a transmission of up to the first 20 bytes of the characteristic value.\n
* A notification will not block; it is a fire and forget.
* @param[in] is_notification if true sends a notification, false sends an indication.
*/
void NimBLECharacteristic::notify(bool is_notification) {
NIMBLE_LOGD(LOG_TAG, ">> notify: length: %d", getDataLength());
NimBLE2902* p2902 = (NimBLE2902*)getDescriptorByUUID(uint16_t(0x2902));
if(p2902 == nullptr) {
if(!(m_properties & NIMBLE_PROPERTY::NOTIFY) &&
!(m_properties & NIMBLE_PROPERTY::INDICATE))
{
NIMBLE_LOGE(LOG_TAG,
"<< notify-Error; Notify/indicate not enabled for characterisitc: %s",
std::string(getUUID()).c_str());
}
if (p2902->m_subscribedVec.size() == 0) {
if (m_subscribedVec.size() == 0) {
NIMBLE_LOGD(LOG_TAG, "<< notify: No clients subscribed.");
return;
}
@ -355,18 +364,18 @@ void NimBLECharacteristic::notify(bool is_notification) {
(m_properties & BLE_GATT_CHR_F_READ_ENC);
int rc = 0;
for (auto &it : p2902->m_subscribedVec) {
uint16_t _mtu = getService()->getServer()->getPeerMTU(it.conn_id);
for (auto &it : m_subscribedVec) {
uint16_t _mtu = getService()->getServer()->getPeerMTU(it.first);
// check if connected and subscribed
if(_mtu == 0 || it.sub_val == 0) {
if(_mtu == 0 || it.second == 0) {
continue;
}
// check if security requirements are satisfied
if(reqSec) {
struct ble_gap_conn_desc desc;
rc = ble_gap_conn_find(it.conn_id, &desc);
rc = ble_gap_conn_find(it.first, &desc);
if(rc != 0 || !desc.sec_state.encrypted) {
continue;
}
@ -376,13 +385,13 @@ void NimBLECharacteristic::notify(bool is_notification) {
NIMBLE_LOGW(LOG_TAG, "- Truncating to %d bytes (maximum notify size)", _mtu - 3);
}
if(is_notification && (!(it.sub_val & NIMBLE_DESC_FLAG_NOTIFY))) {
if(is_notification && (!(it.second & NIMBLE_SUB_NOTIFY))) {
NIMBLE_LOGW(LOG_TAG,
"Sending notification to client subscribed to indications, sending indication instead");
is_notification = false;
}
if(!is_notification && (!(it.sub_val & NIMBLE_DESC_FLAG_INDICATE))) {
if(!is_notification && (!(it.second & NIMBLE_SUB_INDICATE))) {
NIMBLE_LOGW(LOG_TAG,
"Sending indication to client subscribed to notification, sending notification instead");
is_notification = true;
@ -399,7 +408,7 @@ void NimBLECharacteristic::notify(bool is_notification) {
ble_task_data_t taskData = {nullptr, xTaskGetCurrentTaskHandle(),0, nullptr};
m_pTaskData = &taskData;
rc = ble_gattc_indicate_custom(it.conn_id, m_handle, om);
rc = ble_gattc_indicate_custom(it.first, m_handle, om);
if(rc != 0){
statusRC = NimBLECharacteristicCallbacks::Status::ERROR_GATT;
} else {
@ -418,7 +427,7 @@ void NimBLECharacteristic::notify(bool is_notification) {
statusRC = NimBLECharacteristicCallbacks::Status::ERROR_INDICATE_FAILURE;
}
} else {
rc = ble_gattc_notify_custom(it.conn_id, m_handle, om);
rc = ble_gattc_notify_custom(it.first, m_handle, om);
if(rc == 0) {
statusRC = NimBLECharacteristicCallbacks::Status::SUCCESS_NOTIFY;
} else {
@ -435,7 +444,8 @@ void NimBLECharacteristic::notify(bool is_notification) {
/**
* @brief Set the callback handlers for this characteristic.
* @param [in] pCallbacks An instance of a callbacks structure used to define any callbacks for the characteristic.
* @param [in] pCallbacks An instance of a NimBLECharacteristicCallbacks class\n
* used to define any callbacks for the characteristic.
*/
void NimBLECharacteristic::setCallbacks(NimBLECharacteristicCallbacks* pCallbacks) {
if (pCallbacks != nullptr){
@ -473,11 +483,9 @@ void NimBLECharacteristic::setValue(const uint8_t* data, size_t length) {
/**
* @brief Set the value of the characteristic from string data.
* We set the value of the characteristic from the bytes contained in the
* string.
* @param [in] Set the value of the characteristic.
* @return N/A.
* @brief Set the value of the characteristic from string data.\n
* We set the value of the characteristic from the bytes contained in the string.
* @param [in] value the std::string value of the characteristic.
*/
void NimBLECharacteristic::setValue(const std::string &value) {
setValue((uint8_t*)(value.data()), value.length());
@ -515,6 +523,14 @@ void NimBLECharacteristicCallbacks::onRead(NimBLECharacteristic* pCharacteristic
NIMBLE_LOGD("NimBLECharacteristicCallbacks", "onRead: default");
} // onRead
/**
* @brief Callback function to support a read request.
* @param [in] pCharacteristic The characteristic that is the source of the event.
* @param [in] desc The connection description struct that is associated with the peer that performed the read.
*/
void NimBLECharacteristicCallbacks::onRead(NimBLECharacteristic* pCharacteristic, ble_gap_conn_desc* desc) {
NIMBLE_LOGD("NimBLECharacteristicCallbacks", "onRead: default");
} // onRead
/**
* @brief Callback function to support a write request.
@ -524,6 +540,14 @@ void NimBLECharacteristicCallbacks::onWrite(NimBLECharacteristic* pCharacteristi
NIMBLE_LOGD("NimBLECharacteristicCallbacks", "onWrite: default");
} // onWrite
/**
* @brief Callback function to support a write request.
* @param [in] pCharacteristic The characteristic that is the source of the event.
* @param [in] desc The connection description struct that is associated with the peer that performed the write.
*/
void NimBLECharacteristicCallbacks::onWrite(NimBLECharacteristic* pCharacteristic, ble_gap_conn_desc* desc) {
NIMBLE_LOGD("NimBLECharacteristicCallbacks", "onWrite: default");
} // onWrite
/**
* @brief Callback function to support a Notify request.
@ -537,12 +561,31 @@ void NimBLECharacteristicCallbacks::onNotify(NimBLECharacteristic* pCharacterist
/**
* @brief Callback function to support a Notify/Indicate Status report.
* @param [in] pCharacteristic The characteristic that is the source of the event.
* @param [in] s Status of the notification/indication
* @param [in] code Additional code of underlying errors
* @param [in] s Status of the notification/indication.
* @param [in] code Additional return code from the NimBLE stack.
*/
void NimBLECharacteristicCallbacks::onStatus(NimBLECharacteristic* pCharacteristic, Status s, int code) {
NIMBLE_LOGD("NimBLECharacteristicCallbacks", "onStatus: default");
} // onStatus
/**
* @brief Callback function called when a client changes subscription status.
* @param [in] pCharacteristic The characteristic that is the source of the event.
* @param [in] desc The connection description struct that is associated with the client.
* @param [in] subValue The subscription status:
* * 0 = Un-Subscribed
* * 1 = Notifications
* * 2 = Indications
* * 3 = Notifications and Indications
*/
void NimBLECharacteristicCallbacks::onSubscribe(NimBLECharacteristic* pCharacteristic,
ble_gap_conn_desc* desc,
uint16_t subValue)
{
NIMBLE_LOGD("NimBLECharacteristicCallbacks", "onSubscribe: default");
}
#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#endif /* CONFIG_BT_ENABLED */

View File

@ -75,6 +75,15 @@ public:
NimBLEUUID getUUID();
std::string getValue(time_t *timestamp = nullptr);
/**
* @brief A template to convert the characteristic data to <type\>.
* @tparam T The type to convert the data to.
* @param [in] timestamp A pointer to a time_t struct to store the time the value was read.
* @param [in] skipSizeCheck If true it will skip checking if the data size is less than <tt>sizeof(<type\>)</tt>.
* @return The data converted to <type\> or NULL if skipSizeCheck is false and the data is
* less than <tt>sizeof(<type\>)</tt>.
* @details <b>Use:</b> <tt>getValue<type>(&timestamp, skipSizeCheck);</tt>
*/
template<typename T>
T getValue(time_t *timestamp = nullptr, bool skipSizeCheck = false) {
std::string value = getValue();
@ -90,6 +99,10 @@ public:
void setValue(const uint8_t* data, size_t size);
void setValue(const std::string &value);
/**
* @brief Convenience template to set the characteristic value to <type\>val.
* @param [in] s The value to set.
*/
template<typename T>
void setValue(const T &s) {
setValue((uint8_t*)&s, sizeof(T));
@ -97,6 +110,7 @@ public:
std::string toString();
uint16_t getHandle();
size_t getSubscribedCount();
private:
@ -132,6 +146,8 @@ private:
ble_task_data_t *m_pTaskData;
portMUX_TYPE m_valMux;
time_t m_timestamp;
std::vector<std::pair<uint16_t, uint16_t>> m_subscribedVec;
}; // NimBLECharacteristic
@ -144,6 +160,12 @@ private:
*/
class NimBLECharacteristicCallbacks {
public:
/**
* @brief An enum to provide the callback the status of the
* notification/indication, implemented for backward compatibility.
* @deprecated To be removed in the future as the NimBLE stack return code is also provided.
*/
typedef enum {
SUCCESS_INDICATE,
SUCCESS_NOTIFY,
@ -157,9 +179,12 @@ public:
virtual ~NimBLECharacteristicCallbacks();
virtual void onRead(NimBLECharacteristic* pCharacteristic);
virtual void onRead(NimBLECharacteristic* pCharacteristic, ble_gap_conn_desc* desc);
virtual void onWrite(NimBLECharacteristic* pCharacteristic);
virtual void onWrite(NimBLECharacteristic* pCharacteristic, ble_gap_conn_desc* desc);
virtual void onNotify(NimBLECharacteristic* pCharacteristic);
virtual void onStatus(NimBLECharacteristic* pCharacteristic, Status s, int code);
virtual void onSubscribe(NimBLECharacteristic* pCharacteristic, ble_gap_conn_desc* desc, uint16_t subValue);
};
#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)

View File

@ -48,8 +48,12 @@ static NimBLEClientCallbacks defaultCallbacks;
*
*/
NimBLEClient::NimBLEClient()
{
/**
* @brief Constructor, private - only callable by NimBLEDevice::createClient
* to ensure proper handling of the list of client objects.
*/
NimBLEClient::NimBLEClient(const NimBLEAddress &peerAddress) : m_peerAddress(peerAddress) {
m_pClientCallbacks = &defaultCallbacks;
m_conn_id = BLE_HS_CONN_HANDLE_NONE;
m_isConnected = false;
@ -86,7 +90,7 @@ NimBLEClient::~NimBLEClient() {
/**
* @brief Delete any existing services.
* @brief Delete all service objects created by this client and clear the vector.
*/
void NimBLEClient::deleteServices() {
NIMBLE_LOGD(LOG_TAG, ">> deleteServices");
@ -123,30 +127,36 @@ size_t NimBLEClient::deleteService(const NimBLEUUID &uuid) {
/**
* NOT NEEDED
*/
/*
void NimBLEClient::onHostReset() {
}
*/
/**
* Add overloaded function to ease connect to peer device with not public address
*/
bool NimBLEClient::connect(NimBLEAdvertisedDevice* device, bool refreshServices) {
NimBLEAddress address(device->getAddress());
uint8_t type = device->getAddressType();
return connect(address, type, refreshServices);
}
/**
* @brief Connect to the partner (BLE Server).
* @param [in] address The address of the partner.
* @brief Connect to the BLE Server.
* @param [in] deleteAttibutes If true this will delete any attribute objects this client may already\n
* have created and clears the vectors after successful connection.
* @return True on success.
*/
bool NimBLEClient::connect(const NimBLEAddress &address, uint8_t type, bool refreshServices) {
bool NimBLEClient::connect(bool deleteAttibutes) {
return connect(m_peerAddress, deleteAttibutes);
}
/**
* @brief Connect to an advertising device.
* @param [in] device The device to connect to.
* @param [in] deleteAttibutes If true this will delete any attribute objects this client may already\n
* have created and clears the vectors after successful connection.
* @return True on success.
*/
bool NimBLEClient::connect(NimBLEAdvertisedDevice* device, bool deleteAttibutes) {
NimBLEAddress address(device->getAddress());
return connect(address, deleteAttibutes);
}
/**
* @brief Connect to the BLE Server.
* @param [in] address The address of the server.
* @param [in] deleteAttibutes If true this will delete any attribute objects this client may already\n
* have created and clears the vectors after successful connection.
* @return True on success.
*/
bool NimBLEClient::connect(const NimBLEAddress &address, bool deleteAttibutes) {
NIMBLE_LOGD(LOG_TAG, ">> connect(%s)", address.toString().c_str());
if(!NimBLEDevice::m_synced) {
@ -163,17 +173,23 @@ bool NimBLEClient::connect(const NimBLEAddress &address, uint8_t type, bool refr
return false;
}
int rc = 0;
m_peerAddress = address;
if(address == NimBLEAddress("")) {
NIMBLE_LOGE(LOG_TAG, "Invalid peer address;(NULL)");
return false;
} else if(m_peerAddress != address) {
m_peerAddress = address;
}
ble_addr_t peerAddrt;
memcpy(&peerAddrt.val, address.getNative(),6);
peerAddrt.type = type;
memcpy(&peerAddrt.val, m_peerAddress.getNative(),6);
peerAddrt.type = m_peerAddress.getType();
ble_task_data_t taskData = {this, xTaskGetCurrentTaskHandle(), 0, nullptr};
m_pTaskData = &taskData;
/** Try to connect the the advertiser. Allow 30 seconds (30000 ms) for
int rc = 0;
/* Try to connect the the advertiser. Allow 30 seconds (30000 ms) for
* timeout (default value of m_connectTimeout).
* Loop on BLE_HS_EBUSY if the scan hasn't stopped yet.
*/
@ -186,10 +202,9 @@ bool NimBLEClient::connect(const NimBLEAddress &address, uint8_t type, bool refr
}while(rc == BLE_HS_EBUSY);
if (rc != 0 && rc != BLE_HS_EDONE) {
NIMBLE_LOGE(LOG_TAG, "Error: Failed to connect to device; addr_type=%d "
NIMBLE_LOGE(LOG_TAG, "Error: Failed to connect to device; "
"addr=%s, rc=%d; %s",
type,
m_peerAddress.toString().c_str(),
std::string(m_peerAddress).c_str(),
rc, NimBLEUtils::returnCodeToString(rc));
m_pTaskData = nullptr;
m_waitingToConnect = false;
@ -205,8 +220,7 @@ bool NimBLEClient::connect(const NimBLEAddress &address, uint8_t type, bool refr
return false;
}
if(refreshServices) {
NIMBLE_LOGD(LOG_TAG, "Refreshing Services for: (%s)", address.toString().c_str());
if(deleteAttibutes) {
deleteServices();
}
@ -218,33 +232,38 @@ bool NimBLEClient::connect(const NimBLEAddress &address, uint8_t type, bool refr
/**
* @brief Called when a characteristic or descriptor requires encryption or authentication to access it.
* This will pair with the device and bond if enabled.
* @brief Initiate a secure connection (pair/bond) with the server.\n
* Called automatically when a characteristic or descriptor requires encryption or authentication to access it.
* @return True on success.
*/
bool NimBLEClient::secureConnection() {
ble_task_data_t taskData = {this, xTaskGetCurrentTaskHandle(), 0, nullptr};
m_pTaskData = &taskData;
int rc = NimBLEDevice::startSecurity(m_conn_id);
if(rc != 0){
m_pTaskData = nullptr;
return false;
}
int retryCount = 1;
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
do {
m_pTaskData = &taskData;
int rc = NimBLEDevice::startSecurity(m_conn_id);
if(rc != 0){
m_pTaskData = nullptr;
return false;
}
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
} while (taskData.rc == (BLE_HS_ERR_HCI_BASE + BLE_ERR_PINKEY_MISSING) && retryCount--);
if(taskData.rc != 0){
return false;
}
return true;
}
} // secureConnection
/**
* @brief Disconnect from the peer.
* @return N/A.
* @return Error code from NimBLE stack, 0 = success.
*/
int NimBLEClient::disconnect(uint8_t reason) {
NIMBLE_LOGD(LOG_TAG, ">> disconnect()");
@ -264,6 +283,12 @@ int NimBLEClient::disconnect(uint8_t reason) {
/**
* @brief Set the connection paramaters to use when connecting to a server.
* @param [in] minInterval minimum connection interval in 0.625ms units.
* @param [in] maxInterval maximum connection interval in 0.625ms units.
* @param [in] latency number of packets allowed to skip (extends max interval)
* @param [in] timeout the timeout time in 10ms units before disconnecting
* @param [in] scanInterval the scan interval to use when attempting to connect in 0.625ms units.
* @param [in] scanWindow the scan window to use when attempting to connect in 0.625ms units.
*/
void NimBLEClient::setConnectionParams(uint16_t minInterval, uint16_t maxInterval,
uint16_t latency, uint16_t timeout,
@ -271,12 +296,12 @@ void NimBLEClient::setConnectionParams(uint16_t minInterval, uint16_t maxInterva
uint16_t minConnTime, uint16_t maxConnTime)*/
{
m_pConnParams.scan_itvl = scanInterval; // Scan interval in 0.625ms units
m_pConnParams.scan_window = scanWindow; // Scan window in 0.625ms units
m_pConnParams.itvl_min = minInterval; // min_int = 0x10*1.25ms = 20ms
m_pConnParams.itvl_max = maxInterval; // max_int = 0x20*1.25ms = 40ms
m_pConnParams.latency = latency; // number of packets allowed to skip (extends max interval)
m_pConnParams.supervision_timeout = timeout; // timeout = 400*10ms = 4000ms
m_pConnParams.scan_itvl = scanInterval;
m_pConnParams.scan_window = scanWindow;
m_pConnParams.itvl_min = minInterval;
m_pConnParams.itvl_max = maxInterval;
m_pConnParams.latency = latency;
m_pConnParams.supervision_timeout = timeout;
// These are not used by NimBLE at this time - Must leave at defaults
//m_pConnParams->min_ce_len = minConnTime; // Minimum length of connection event in 0.625ms units
@ -284,11 +309,16 @@ void NimBLEClient::setConnectionParams(uint16_t minInterval, uint16_t maxInterva
int rc = NimBLEUtils::checkConnParams(&m_pConnParams);
assert(rc == 0 && "Invalid Connection parameters");
}
} // setConnectionParams
/**
* Update connection parameters can be called only after connection has been established
* @brief Update the connection parameters:
* * Can only be used after a connection has been established.
* @param [in] minInterval minimum connection interval in 0.625ms units.
* @param [in] maxInterval maximum connection interval in 0.625ms units.
* @param [in] latency number of packets allowed to skip (extends max interval)
* @param [in] timeout the timeout time in 10ms units before disconnecting
*/
void NimBLEClient::updateConnParams(uint16_t minInterval, uint16_t maxInterval,
uint16_t latency, uint16_t timeout)
@ -308,16 +338,16 @@ void NimBLEClient::updateConnParams(uint16_t minInterval, uint16_t maxInterval,
NIMBLE_LOGE(LOG_TAG, "Update params error: %d, %s",
rc, NimBLEUtils::returnCodeToString(rc));
}
}
} // updateConnParams
/**
* @brief Set the timeout to wait for connection attempt to complete
* @params[in] Time to wait in seconds.
* @brief Set the timeout to wait for connection attempt to complete.
* @param [in] time The number of seconds before timeout.
*/
void NimBLEClient::setConnectTimeout(uint8_t time) {
m_connectTimeout = (uint32_t)(time * 1000);
}
} // setConnectTimeout
/**
@ -334,7 +364,23 @@ uint16_t NimBLEClient::getConnId() {
*/
NimBLEAddress NimBLEClient::getPeerAddress() {
return m_peerAddress;
} // getAddress
} // getPeerAddress
/**
* @brief Set the peer address.
* @param [in] address The address of the peer that this client is
* connected or should connect to.
*/
void NimBLEClient::setPeerAddress(const NimBLEAddress &address) {
if(isConnected()) {
NIMBLE_LOGE(LOG_TAG, "Cannot set peer address while connected");
return;
}
m_peerAddress = address;
NIMBLE_LOGD(LOG_TAG, "Peer address set: %s", std::string(m_peerAddress).c_str());
} // setPeerAddress
/**
@ -381,7 +427,7 @@ std::vector<NimBLERemoteService*>::iterator NimBLEClient::end() {
/**
* @brief Get the service BLE Remote Service instance corresponding to the uuid.
* @param [in] uuid The UUID of the service being sought.
* @return A reference to the Service or nullptr if don't know about it.
* @return A pointer to the service or nullptr if not found.
*/
NimBLERemoteService* NimBLEClient::getService(const char* uuid) {
return getService(NimBLEUUID(uuid));
@ -391,7 +437,7 @@ NimBLERemoteService* NimBLEClient::getService(const char* uuid) {
/**
* @brief Get the service object corresponding to the uuid.
* @param [in] uuid The UUID of the service being sought.
* @return A reference to the Service or nullptr if don't know about it.
* @return A pointer to the service or nullptr if not found.
*/
NimBLERemoteService* NimBLEClient::getService(const NimBLEUUID &uuid) {
NIMBLE_LOGD(LOG_TAG, ">> getService: uuid: %s", uuid.toString().c_str());
@ -416,12 +462,11 @@ NimBLERemoteService* NimBLEClient::getService(const NimBLEUUID &uuid) {
/**
* @Get a pointer to the vector of found services.
* @param [in] bool value to indicate if the current vector should be cleared and
* subsequently all services retrieved from the peripheral.
* If false the vector will be returned with the currently stored services,
* If true it will retrieve all services from the peripheral and return the vector with all services
* @return a pointer to the vector of available services.
* @brief Get a pointer to the vector of found services.
* @param [in] refresh If true the current services vector will be cleared and\n
* all services will be retrieved from the peripheral.\n
* If false the vector will be returned with the currently stored services.
* @return A pointer to the vector of available services.
*/
std::vector<NimBLERemoteService*>* NimBLEClient::getServices(bool refresh) {
if(refresh) {
@ -435,11 +480,11 @@ std::vector<NimBLERemoteService*>* NimBLEClient::getServices(bool refresh) {
}
}
return &m_servicesVector;
}
} // getServices
/**
* @ Retrieves the full database of attributes that the peripheral has available.
* @brief Retrieves the full database of attributes that the peripheral has available.
*/
void NimBLEClient::discoverAttributes() {
for(auto svc: *getServices(true)) {
@ -447,14 +492,12 @@ void NimBLEClient::discoverAttributes() {
chr->getDescriptors(true);
}
}
}
} // discoverAttributes
/**
* @brief Ask the remote %BLE server for its services.
* A %BLE Server exposes a set of services for its partners. Here we ask the server for its set of
* services and wait until we have received them all.
* We then ask for the characteristics for each service found and their desciptors.
* @brief Ask the remote %BLE server for its services.\n
* Here we ask the server for its set of services and wait until we have received them all.
* @return true on success otherwise false if an error occurred
*/
bool NimBLEClient::retrieveServices(const NimBLEUUID *uuid_filter) {
@ -502,7 +545,7 @@ bool NimBLEClient::retrieveServices(const NimBLEUUID *uuid_filter) {
/**
* @brief STATIC Callback for the service discovery API function.
* @brief STATIC Callback for the service discovery API function.\n
* When a service is found or there is none left or there was an error
* the API will call this and report findings.
*/
@ -574,6 +617,7 @@ std::string NimBLEClient::getValue(const NimBLEUUID &serviceUUID, const NimBLEUU
* @brief Set the value of a specific characteristic associated with a specific service.
* @param [in] serviceUUID The service that owns the characteristic.
* @param [in] characteristicUUID The characteristic whose value we wish to write.
* @param [in] value The value to write to the characteristic.
* @returns true if successful otherwise false
*/
bool NimBLEClient::setValue(const NimBLEUUID &serviceUUID, const NimBLEUUID &characteristicUUID,
@ -600,17 +644,17 @@ bool NimBLEClient::setValue(const NimBLEUUID &serviceUUID, const NimBLEUUID &cha
/**
* @brief Get the current mtu of this connection.
* @returns The MTU value.
*/
uint16_t NimBLEClient::getMTU() {
return ble_att_mtu(m_conn_id);
}
} // getMTU
/**
* @brief Handle a received GAP event.
*
* @param [in] event
* @param [in] arg = pointer to the client instance
* @param [in] event The event structure sent by the NimBLE stack.
* @param [in] arg A pointer to the client instance that registered for this callback.
*/
/*STATIC*/ int NimBLEClient::handleGapEvent(struct ble_gap_event *event, void *arg) {
NimBLEClient* client = (NimBLEClient*)arg;
@ -783,12 +827,15 @@ uint16_t NimBLEClient::getMTU() {
return 0;
}
if(event->enc_change.status == 0) {
if(event->enc_change.status == 0 || event->enc_change.status == (BLE_HS_ERR_HCI_BASE + BLE_ERR_PINKEY_MISSING)) {
struct ble_gap_conn_desc desc;
rc = ble_gap_conn_find(event->conn_update.conn_handle, &desc);
rc = ble_gap_conn_find(event->enc_change.conn_handle, &desc);
assert(rc == 0);
if(NimBLEDevice::m_securityCallbacks != nullptr) {
if (event->enc_change.status == (BLE_HS_ERR_HCI_BASE + BLE_ERR_PINKEY_MISSING)) {
// Key is missing, try deleting.
ble_store_util_delete_peer(&desc.peer_id_addr);
} else if(NimBLEDevice::m_securityCallbacks != nullptr) {
NimBLEDevice::m_securityCallbacks->onAuthenticationComplete(&desc);
} else {
client->m_pClientCallbacks->onAuthenticationComplete(&desc);
@ -855,7 +902,7 @@ uint16_t NimBLEClient::getMTU() {
pkey.passkey = NimBLEDevice::m_securityCallbacks->onPassKeyRequest();
/////////////////////////////////////////////
} else {
client->m_pClientCallbacks->onPassKeyRequest();
pkey.passkey = client->m_pClientCallbacks->onPassKeyRequest();
}
rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey);
@ -893,7 +940,9 @@ bool NimBLEClient::isConnected() {
/**
* @brief Set the callbacks that will be invoked.
* @brief Set the callbacks that will be invoked when events are received.
* @param [in] pClientCallbacks A pointer to a class to receive the event callbacks.
* @param [in] deleteCallbacks If true this will delete the callback class sent when the client is destructed.
*/
void NimBLEClient::setClientCallbacks(NimBLEClientCallbacks* pClientCallbacks, bool deleteCallbacks) {
if (pClientCallbacks != nullptr){
@ -938,7 +987,7 @@ uint32_t NimBLEClientCallbacks::onPassKeyRequest(){
NIMBLE_LOGD("NimBLEClientCallbacks", "onPassKeyRequest: default: 123456");
return 123456;
}
/*
void NimBLEClientCallbacks::onPassKeyNotify(uint32_t pass_key){
NIMBLE_LOGD("NimBLEClientCallbacks", "onPassKeyNotify: default: %d", pass_key);
}
@ -946,7 +995,7 @@ void NimBLEClientCallbacks::onPassKeyNotify(uint32_t pass_key){
bool NimBLEClientCallbacks::onSecurityRequest(){
NIMBLE_LOGD("NimBLEClientCallbacks", "onSecurityRequest: default: true");
return true;
}
}*/
void NimBLEClientCallbacks::onAuthenticationComplete(ble_gap_conn_desc* desc){
NIMBLE_LOGD("NimBLEClientCallbacks", "onAuthenticationComplete: default");
}

View File

@ -38,11 +38,12 @@ class NimBLEAdvertisedDevice;
*/
class NimBLEClient {
public:
bool connect(NimBLEAdvertisedDevice* device, bool refreshServices = true);
bool connect(const NimBLEAddress &address, uint8_t type = BLE_ADDR_PUBLIC,
bool refreshServices = true);
bool connect(NimBLEAdvertisedDevice* device, bool deleteAttibutes = true);
bool connect(const NimBLEAddress &address, bool deleteAttibutes = true);
bool connect(bool deleteAttibutes = true);
int disconnect(uint8_t reason = BLE_ERR_REM_USER_CONN_TERM);
NimBLEAddress getPeerAddress();
void setPeerAddress(const NimBLEAddress &address);
int getRssi();
std::vector<NimBLERemoteService*>* getServices(bool refresh = false);
std::vector<NimBLERemoteService*>::iterator begin();
@ -70,7 +71,7 @@ public:
void discoverAttributes();
private:
NimBLEClient();
NimBLEClient(const NimBLEAddress &peerAddress);
~NimBLEClient();
friend class NimBLEDevice;
@ -83,7 +84,7 @@ private:
void *arg);
bool retrieveServices(const NimBLEUUID *uuid_filter = nullptr);
NimBLEAddress m_peerAddress = NimBLEAddress("");
NimBLEAddress m_peerAddress;
uint16_t m_conn_id;
bool m_isConnected;
bool m_waitingToConnect;
@ -107,13 +108,48 @@ private:
class NimBLEClientCallbacks {
public:
virtual ~NimBLEClientCallbacks() {};
/**
* @brief Called after client connects.
* @param [in] pClient A pointer to the calling client object.
*/
virtual void onConnect(NimBLEClient* pClient);
/**
* @brief Called when disconnected from the server.
* @param [in] pClient A pointer to the calling client object.
*/
virtual void onDisconnect(NimBLEClient* pClient);
/**
* @brief Called when server requests to update the connection parameters.
* @param [in] pClient A pointer to the calling client object.
* @param [in] params A pointer to the struct containing the connection parameters requested.
* @return True to accept the parmeters.
*/
virtual bool onConnParamsUpdateRequest(NimBLEClient* pClient, const ble_gap_upd_params* params);
/**
* @brief Called when server requests a passkey for pairing.
* @return The passkey to be sent to the server.
*/
virtual uint32_t onPassKeyRequest();
virtual void onPassKeyNotify(uint32_t pass_key);
virtual bool onSecurityRequest();
/*virtual void onPassKeyNotify(uint32_t pass_key);
virtual bool onSecurityRequest();*/
/**
* @brief Called when the pairing procedure is complete.
* @param [in] desc A pointer to the struct containing the connection information.\n
* This can be used to check the status of the connection encryption/pairing.
*/
virtual void onAuthenticationComplete(ble_gap_conn_desc* desc);
/**
* @brief Called when using numeric comparision for pairing.
* @param [in] pin The pin to compare with the server.
* @return True to accept the pin.
*/
virtual bool onConfirmPIN(uint32_t pin);
};

View File

@ -52,6 +52,10 @@ public:
void setValue(const std::string &value);
std::string toString();
/**
* @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) {
setValue((uint8_t*)&s, sizeof(T));
@ -101,6 +105,8 @@ public:
virtual void onWrite(NimBLEDescriptor* pDescriptor);
};
#include "NimBLE2904.h"
#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#endif /* CONFIG_BT_ENABLED */
#endif /* MAIN_NIMBLEDESCRIPTOR_H_ */

View File

@ -90,6 +90,10 @@ NimBLESecurityCallbacks* NimBLEDevice::m_securityCallbacks = nullptr;
#if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
/**
* @brief Get the instance of the advertising object.
* @return A pointer to the advertising object.
*/
NimBLEAdvertising* NimBLEDevice::getAdvertising() {
if(m_bleAdvertising == nullptr) {
m_bleAdvertising = new NimBLEAdvertising();
@ -98,11 +102,17 @@ NimBLEAdvertising* NimBLEDevice::getAdvertising() {
}
/**
* @brief Convenience function to begin advertising.
*/
void NimBLEDevice::startAdvertising() {
getAdvertising()->start();
} // startAdvertising
/**
* @brief Convenience function to stop advertising.
*/
void NimBLEDevice::stopAdvertising() {
getAdvertising()->stop();
} // stopAdvertising
@ -123,19 +133,22 @@ void NimBLEDevice::stopAdvertising() {
} // getScan
#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
/**
* @brief Creates a new client object and maintains a list of all client objects
* each client can connect to 1 peripheral device.
* @param [in] peerAddress An optional peer address that is copied to the new client
* object, allows for calling NimBLEClient::connect(bool) without a device or address parameter.
* @return A reference to the new client object.
*/
#if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
/* STATIC */ NimBLEClient* NimBLEDevice::createClient() {
/* STATIC */ NimBLEClient* NimBLEDevice::createClient(NimBLEAddress peerAddress) {
if(m_cList.size() >= NIMBLE_MAX_CONNECTIONS) {
NIMBLE_LOGW("Number of clients exceeds Max connections. Max=(%d)",
NIMBLE_MAX_CONNECTIONS);
}
NimBLEClient* pClient = new NimBLEClient();
NimBLEClient* pClient = new NimBLEClient(peerAddress);
m_cList.push_back(pClient);
return pClient;
@ -143,9 +156,9 @@ void NimBLEDevice::stopAdvertising() {
/**
* @brief Delete the client object and remove it from the list.
* Check if it is connected or trying to connect and close/stop it first.
* @param [in] Pointer to the client object.
* @brief Delete the client object and remove it from the list.\n
* Checks if it is connected or trying to connect and disconnects/stops it first.
* @param [in] pClient A pointer to the client object.
*/
/* STATIC */ bool NimBLEDevice::deleteClient(NimBLEClient* pClient) {
if(pClient == nullptr) {
@ -183,8 +196,8 @@ void NimBLEDevice::stopAdvertising() {
/**
* @brief get the list of clients.
* @return a pointer to the list of clients.
* @brief Get the list of created client objects.
* @return A pointer to the list of clients.
*/
/* STATIC */std::list<NimBLEClient*>* NimBLEDevice::getClientList() {
return &m_cList;
@ -192,8 +205,8 @@ void NimBLEDevice::stopAdvertising() {
/**
* @brief get the size of the list of clients.
* @return a pointer to the list of clients.
* @brief Get the number of created client objects.
* @return Number of client objects created.
*/
/* STATIC */size_t NimBLEDevice::getClientListSize() {
return m_cList.size();
@ -202,8 +215,8 @@ void NimBLEDevice::stopAdvertising() {
/**
* @brief Get a reference to a client by connection ID.
* @param [in] The client connection ID to search for.
* @return A reference pointer to the client with the spcified connection ID.
* @param [in] conn_id The client connection ID to search for.
* @return A pointer to the client object with the spcified connection ID.
*/
/* STATIC */NimBLEClient* NimBLEDevice::getClientByID(uint16_t conn_id) {
for(auto it = m_cList.cbegin(); it != m_cList.cend(); ++it) {
@ -218,8 +231,8 @@ void NimBLEDevice::stopAdvertising() {
/**
* @brief Get a reference to a client by peer address.
* @param [in] a NimBLEAddress of the peer to search for.
* @return A reference pointer to the client with the peer address.
* @param [in] peer_addr The address of the peer to search for.
* @return A pointer to the client object with the peer address.
*/
/* STATIC */NimBLEClient* NimBLEDevice::getClientByPeerAddress(const NimBLEAddress &peer_addr) {
for(auto it = m_cList.cbegin(); it != m_cList.cend(); ++it) {
@ -233,7 +246,7 @@ void NimBLEDevice::stopAdvertising() {
/**
* @brief Finds the first disconnected client in the list.
* @return A reference pointer to the first client that is not connected to a peer.
* @return A pointer to the first client object that is not connected to a peer.
*/
/* STATIC */NimBLEClient* NimBLEDevice::getDisconnectedClient() {
for(auto it = m_cList.cbegin(); it != m_cList.cend(); ++it) {
@ -249,16 +262,28 @@ void NimBLEDevice::stopAdvertising() {
/**
* @brief Set the transmission power.
* The power level can be one of:
* * ESP_PWR_LVL_N12 = 0, !< Corresponding to -12dbm
* * ESP_PWR_LVL_N9 = 1, !< Corresponding to -9dbm
* * ESP_PWR_LVL_N6 = 2, !< Corresponding to -6dbm
* * ESP_PWR_LVL_N3 = 3, !< Corresponding to -3dbm
* * ESP_PWR_LVL_N0 = 4, !< Corresponding to 0dbm
* * ESP_PWR_LVL_P3 = 5, !< Corresponding to +3dbm
* * ESP_PWR_LVL_P6 = 6, !< Corresponding to +6dbm
* * ESP_PWR_LVL_P9 = 7, !< Corresponding to +9dbm
* @param [in] powerLevel.
* @param [in] powerLevel The power level to set, can be one of:
* * ESP_PWR_LVL_N12 = 0, Corresponding to -12dbm
* * ESP_PWR_LVL_N9 = 1, Corresponding to -9dbm
* * ESP_PWR_LVL_N6 = 2, Corresponding to -6dbm
* * ESP_PWR_LVL_N3 = 3, Corresponding to -3dbm
* * ESP_PWR_LVL_N0 = 4, Corresponding to 0dbm
* * ESP_PWR_LVL_P3 = 5, Corresponding to +3dbm
* * ESP_PWR_LVL_P6 = 6, Corresponding to +6dbm
* * ESP_PWR_LVL_P9 = 7, Corresponding to +9dbm
* @param [in] powerType The BLE function to set the power level for, can be one of:
* * ESP_BLE_PWR_TYPE_CONN_HDL0 = 0, For connection handle 0
* * ESP_BLE_PWR_TYPE_CONN_HDL1 = 1, For connection handle 1
* * ESP_BLE_PWR_TYPE_CONN_HDL2 = 2, For connection handle 2
* * ESP_BLE_PWR_TYPE_CONN_HDL3 = 3, For connection handle 3
* * ESP_BLE_PWR_TYPE_CONN_HDL4 = 4, For connection handle 4
* * ESP_BLE_PWR_TYPE_CONN_HDL5 = 5, For connection handle 5
* * ESP_BLE_PWR_TYPE_CONN_HDL6 = 6, For connection handle 6
* * ESP_BLE_PWR_TYPE_CONN_HDL7 = 7, For connection handle 7
* * ESP_BLE_PWR_TYPE_CONN_HDL8 = 8, For connection handle 8
* * ESP_BLE_PWR_TYPE_ADV = 9, For advertising
* * ESP_BLE_PWR_TYPE_SCAN = 10, For scan
* * ESP_BLE_PWR_TYPE_DEFAULT = 11, For default, if not set other, it will use default value
*/
/* STATIC */ void NimBLEDevice::setPower(esp_power_level_t powerLevel, esp_ble_power_type_t powerType) {
NIMBLE_LOGD(LOG_TAG, ">> setPower: %d (type: %d)", powerLevel, powerType);
@ -270,6 +295,24 @@ void NimBLEDevice::stopAdvertising() {
} // setPower
/**
* @brief Set the transmission power.
* @param [in] powerType The power level to set, can be one of:
* * ESP_BLE_PWR_TYPE_CONN_HDL0 = 0, For connection handle 0
* * ESP_BLE_PWR_TYPE_CONN_HDL1 = 1, For connection handle 1
* * ESP_BLE_PWR_TYPE_CONN_HDL2 = 2, For connection handle 2
* * ESP_BLE_PWR_TYPE_CONN_HDL3 = 3, For connection handle 3
* * ESP_BLE_PWR_TYPE_CONN_HDL4 = 4, For connection handle 4
* * ESP_BLE_PWR_TYPE_CONN_HDL5 = 5, For connection handle 5
* * ESP_BLE_PWR_TYPE_CONN_HDL6 = 6, For connection handle 6
* * ESP_BLE_PWR_TYPE_CONN_HDL7 = 7, For connection handle 7
* * ESP_BLE_PWR_TYPE_CONN_HDL8 = 8, For connection handle 8
* * ESP_BLE_PWR_TYPE_ADV = 9, For advertising
* * ESP_BLE_PWR_TYPE_SCAN = 10, For scan
* * ESP_BLE_PWR_TYPE_DEFAULT = 11, For default, if not set other, it will use default value
* @return the power level currently used by the type specified.
*/
/* STATIC */ int NimBLEDevice::getPower(esp_ble_power_type_t powerType) {
switch(esp_ble_tx_power_get(powerType)) {
@ -302,7 +345,6 @@ void NimBLEDevice::stopAdvertising() {
*/
/* STATIC*/ NimBLEAddress NimBLEDevice::getAddress() {
ble_addr_t addr = {BLE_ADDR_PUBLIC, 0};
//ble_hs_id_copy_addr(BLE_ADDR_PUBLIC, addr.val, NULL)
if(BLE_HS_ENOADDR == ble_hs_id_copy_addr(BLE_ADDR_PUBLIC, addr.val, NULL)) {
NIMBLE_LOGD(LOG_TAG, "Public address not found, checking random");
@ -324,9 +366,9 @@ void NimBLEDevice::stopAdvertising() {
/**
* @brief Setup local mtu that will be used to negotiate mtu during request from client peer
* @param [in] mtu Value to set local mtu, should be larger than 23 and lower or equal to
* BLE_ATT_MTU_MAX = 527
* @brief Setup local mtu that will be used to negotiate mtu during request from client peer.
* @param [in] mtu Value to set local mtu:
* * This should be larger than 23 and lower or equal to BLE_ATT_MTU_MAX = 527.
*/
/* STATIC */int NimBLEDevice::setMTU(uint16_t mtu) {
NIMBLE_LOGD(LOG_TAG, ">> setLocalMTU: %d", mtu);
@ -344,6 +386,7 @@ void NimBLEDevice::stopAdvertising() {
/**
* @brief Get local MTU value set.
* @return The current preferred MTU setting.
*/
/* STATIC */uint16_t NimBLEDevice::getMTU() {
return ble_att_preferred_mtu();
@ -352,6 +395,7 @@ void NimBLEDevice::stopAdvertising() {
/**
* @brief Host reset, we pass the message so we don't make calls until resynced.
* @param [in] reason The reason code for the reset.
*/
/* STATIC */ void NimBLEDevice::onReset(int reason)
{
@ -389,7 +433,7 @@ void NimBLEDevice::stopAdvertising() {
/**
* @brief Host resynced with controller, all clear to make calls.
* @brief Host resynced with controller, all clear to make calls to the stack.
*/
/* STATIC */ void NimBLEDevice::onSync(void)
{
@ -439,7 +483,7 @@ void NimBLEDevice::stopAdvertising() {
/**
* @brief Initialize the %BLE environment.
* @param deviceName The device name of the device.
* @param [in] deviceName The device name of the device.
*/
/* STATIC */ void NimBLEDevice::init(const std::string &deviceName) {
if(!initialized){
@ -497,8 +541,10 @@ void NimBLEDevice::stopAdvertising() {
/**
* @brief Shutdown the NimBLE stack/controller.
* @param [in] clearAll If true, deletes all server/advertising/scan/client objects after deinitializing.
* @note If clearAll is true when called, any references to the created objects become invalid.
*/
/* STATIC */ void NimBLEDevice::deinit() {
/* STATIC */ void NimBLEDevice::deinit(bool clearAll) {
int ret = nimble_port_stop();
if (ret == 0) {
nimble_port_deinit();
@ -510,12 +556,49 @@ void NimBLEDevice::stopAdvertising() {
initialized = false;
m_synced = false;
if(clearAll) {
#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
if(NimBLEDevice::m_pServer != nullptr) {
delete NimBLEDevice::m_pServer;
NimBLEDevice::m_pServer = nullptr;
}
#endif
#if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
if(NimBLEDevice::m_bleAdvertising != nullptr) {
delete NimBLEDevice::m_bleAdvertising;
NimBLEDevice::m_bleAdvertising = nullptr;
}
#endif
#if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
if(NimBLEDevice::m_pScan != nullptr) {
delete NimBLEDevice::m_pScan;
NimBLEDevice::m_pScan= nullptr;
}
#endif
#if defined( CONFIG_BT_NIMBLE_ROLE_CENTRAL)
for(auto &it : m_cList) {
deleteClient(it);
m_cList.clear();
}
#endif
m_ignoreList.clear();
if(m_securityCallbacks != nullptr) {
delete m_securityCallbacks;
}
}
}
} // deinit
/**
* @brief Check if the initialization is complete.
* @return true if initialized.
*/
bool NimBLEDevice::getInitialized() {
return initialized;
@ -524,9 +607,9 @@ bool NimBLEDevice::getInitialized() {
/**
* @brief Set the authorization mode for this device.
* @param bonding, if true we allow bonding, false no bonding will be performed.
* @param mitm, if true we are capable of man in the middle protection, false if not.
* @param sc, if true we will perform secure connection pairing, false we will use legacy pairing.
* @param bonding If true we allow bonding, false no bonding will be performed.
* @param mitm If true we are capable of man in the middle protection, false if not.
* @param sc If true we will perform secure connection pairing, false we will use legacy pairing.
*/
/*STATIC*/ void NimBLEDevice::setSecurityAuth(bool bonding, bool mitm, bool sc) {
NIMBLE_LOGD(LOG_TAG, "Setting bonding: %d, mitm: %d, sc: %d",bonding,mitm,sc);
@ -538,13 +621,12 @@ bool NimBLEDevice::getInitialized() {
/**
* @brief Set the authorization mode for this device.
* @param A bitmap indicating what modes are supported.
* The bits are defined as follows:
** 0x01 BLE_SM_PAIR_AUTHREQ_BOND
** 0x04 BLE_SM_PAIR_AUTHREQ_MITM
** 0x08 BLE_SM_PAIR_AUTHREQ_SC
** 0x10 BLE_SM_PAIR_AUTHREQ_KEYPRESS - not yet supported.
** 0xe2 BLE_SM_PAIR_AUTHREQ_RESERVED - for reference only.
* @param auth_req A bitmap indicating what modes are supported.\n
* The available bits are defined as:
* * 0x01 BLE_SM_PAIR_AUTHREQ_BOND
* * 0x04 BLE_SM_PAIR_AUTHREQ_MITM
* * 0x08 BLE_SM_PAIR_AUTHREQ_SC
* * 0x10 BLE_SM_PAIR_AUTHREQ_KEYPRESS - not yet supported.
*/
/*STATIC*/void NimBLEDevice::setSecurityAuth(uint8_t auth_req) {
NimBLEDevice::setSecurityAuth((auth_req & BLE_SM_PAIR_AUTHREQ_BOND)>0,
@ -555,12 +637,12 @@ bool NimBLEDevice::getInitialized() {
/**
* @brief Set the Input/Output capabilities of this device.
* @param One of the following:
** 0x00 BLE_HS_IO_DISPLAY_ONLY DisplayOnly IO capability
** 0x01 BLE_HS_IO_DISPLAY_YESNO DisplayYesNo IO capability
** 0x02 BLE_HS_IO_KEYBOARD_ONLY KeyboardOnly IO capability
** 0x03 BLE_HS_IO_NO_INPUT_OUTPUT NoInputNoOutput IO capability
** 0x04 BLE_HS_IO_KEYBOARD_DISPLAY KeyboardDisplay Only IO capability
* @param iocap One of the following values:
* * 0x00 BLE_HS_IO_DISPLAY_ONLY DisplayOnly IO capability
* * 0x01 BLE_HS_IO_DISPLAY_YESNO DisplayYesNo IO capability
* * 0x02 BLE_HS_IO_KEYBOARD_ONLY KeyboardOnly IO capability
* * 0x03 BLE_HS_IO_NO_INPUT_OUTPUT NoInputNoOutput IO capability
* * 0x04 BLE_HS_IO_KEYBOARD_DISPLAY KeyboardDisplay Only IO capability
*/
/*STATIC*/ void NimBLEDevice::setSecurityIOCap(uint8_t iocap) {
ble_hs_cfg.sm_io_cap = iocap;
@ -569,12 +651,12 @@ bool NimBLEDevice::getInitialized() {
/**
* @brief If we are the initiator of the security procedure this sets the keys we will distribute.
* @param A bitmap indicating which keys to distribute during pairing.
* The bits are defined as follows:
** 0x01: BLE_SM_PAIR_KEY_DIST_ENC - Distribute the encryption key.
** 0x02: BLE_SM_PAIR_KEY_DIST_ID - Distribute the ID key (IRK).
** 0x04: BLE_SM_PAIR_KEY_DIST_SIGN
** 0x08: BLE_SM_PAIR_KEY_DIST_LINK
* @param init_key A bitmap indicating which keys to distribute during pairing.\n
* The available bits are defined as:
* * 0x01: BLE_SM_PAIR_KEY_DIST_ENC - Distribute the encryption key.
* * 0x02: BLE_SM_PAIR_KEY_DIST_ID - Distribute the ID key (IRK).
* * 0x04: BLE_SM_PAIR_KEY_DIST_SIGN
* * 0x08: BLE_SM_PAIR_KEY_DIST_LINK
*/
/*STATIC*/void NimBLEDevice::setSecurityInitKey(uint8_t init_key) {
ble_hs_cfg.sm_our_key_dist = init_key;
@ -583,20 +665,21 @@ bool NimBLEDevice::getInitialized() {
/**
* @brief Set the keys we are willing to accept during pairing.
* @param A bitmap indicating which keys to accept during pairing.
* The bits are defined as follows:
** 0x01: BLE_SM_PAIR_KEY_DIST_ENC - Accept the encryption key.
** 0x02: BLE_SM_PAIR_KEY_DIST_ID - Accept the ID key (IRK).
** 0x04: BLE_SM_PAIR_KEY_DIST_SIGN
** 0x08: BLE_SM_PAIR_KEY_DIST_LINK
* @param resp_key A bitmap indicating which keys to accept during pairing.
* The available bits are defined as:
* * 0x01: BLE_SM_PAIR_KEY_DIST_ENC - Accept the encryption key.
* * 0x02: BLE_SM_PAIR_KEY_DIST_ID - Accept the ID key (IRK).
* * 0x04: BLE_SM_PAIR_KEY_DIST_SIGN
* * 0x08: BLE_SM_PAIR_KEY_DIST_LINK
*/
/*STATIC*/void NimBLEDevice::setSecurityRespKey(uint8_t init_key) {
ble_hs_cfg.sm_their_key_dist = init_key;
/*STATIC*/void NimBLEDevice::setSecurityRespKey(uint8_t resp_key) {
ble_hs_cfg.sm_their_key_dist = resp_key;
} // setsSecurityRespKey
/**
* @brief Set the passkey for pairing.
* @brief Set the passkey the server will ask for when pairing.
* @param [in] pin The passkey to use.
*/
/*STATIC*/void NimBLEDevice::setSecurityPasskey(uint32_t pin) {
m_passkey = pin;
@ -604,7 +687,8 @@ bool NimBLEDevice::getInitialized() {
/**
* @brief Get the passkey for pairing.
* @brief Get the current passkey used for pairing.
* @return The current passkey.
*/
/*STATIC*/uint32_t NimBLEDevice::getSecurityPasskey() {
return m_passkey;
@ -613,7 +697,8 @@ bool NimBLEDevice::getInitialized() {
/**
* @brief Set callbacks that will be used to handle encryption negotiation events and authentication events
* @param [in] cllbacks Pointer to NimBLESecurityCallbacks class
* @param [in] callbacks Pointer to NimBLESecurityCallbacks class
* @deprecated For backward compatibility, New code should use client/server callback methods.
*/
void NimBLEDevice::setSecurityCallbacks(NimBLESecurityCallbacks* callbacks) {
NimBLEDevice::m_securityCallbacks = callbacks;
@ -622,8 +707,8 @@ void NimBLEDevice::setSecurityCallbacks(NimBLESecurityCallbacks* callbacks) {
/**
* @brief Start the connection securing and authorization for this connection.
* @param Connection id of the client.
* @returns host return code 0 if success.
* @param conn_id The connection id of the peer device.
* @returns NimBLE stack return code, 0 = success.
*/
/* STATIC */int NimBLEDevice::startSecurity(uint16_t conn_id) {
/* if(m_securityCallbacks != nullptr) {
@ -641,6 +726,7 @@ void NimBLEDevice::setSecurityCallbacks(NimBLESecurityCallbacks* callbacks) {
/**
* @brief Check if the device address is on our ignore list.
* @param [in] address The address to look for.
* @return True if ignoring.
*/
/*STATIC*/ bool NimBLEDevice::isIgnored(const NimBLEAddress &address) {
@ -656,7 +742,7 @@ void NimBLEDevice::setSecurityCallbacks(NimBLESecurityCallbacks* callbacks) {
/**
* @brief Add a device to the ignore list.
* @param Address of the device we want to ignore.
* @param [in] address The address of the device we want to ignore.
*/
/*STATIC*/ void NimBLEDevice::addIgnored(const NimBLEAddress &address) {
m_ignoreList.push_back(address);
@ -665,7 +751,7 @@ void NimBLEDevice::setSecurityCallbacks(NimBLESecurityCallbacks* callbacks) {
/**
* @brief Remove a device from the ignore list.
* @param Address of the device we want to remove from the list.
* @param [in] address The address of the device we want to remove from the list.
*/
/*STATIC*/void NimBLEDevice::removeIgnored(const NimBLEAddress &address) {
for(auto it = m_ignoreList.begin(); it != m_ignoreList.end(); ++it) {
@ -679,6 +765,7 @@ void NimBLEDevice::setSecurityCallbacks(NimBLESecurityCallbacks* callbacks) {
/**
* @brief Set a custom callback for gap events.
* @param [in] handler The function to call when gap events occur.
*/
void NimBLEDevice::setCustomGapHandler(gap_event_handler handler) {
m_customGapHandler = handler;

View File

@ -81,17 +81,17 @@
#define NIMBLE_MAX_CONNECTIONS CONFIG_NIMBLE_MAX_CONNECTIONS
#endif
/**
* @brief BLE functions.
*/
typedef int (*gap_event_handler)(ble_gap_event *event, void *arg);
typedef int (*gap_event_handler)(ble_gap_event *event, void *arg);
extern "C" void ble_store_config_init(void);
/**
* @brief A model of a %BLE Device from which all the BLE roles are created.
*/
class NimBLEDevice {
public:
static void init(const std::string &deviceName);
static void deinit();
static void deinit(bool clearAll = false);
static bool getInitialized();
static NimBLEAddress getAddress();
static std::string toString();
@ -130,7 +130,7 @@ public:
#endif
#if defined( CONFIG_BT_NIMBLE_ROLE_CENTRAL)
static NimBLEClient* createClient();
static NimBLEClient* createClient(NimBLEAddress peerAddress = NimBLEAddress(""));
static bool deleteClient(NimBLEClient* pClient);
static NimBLEClient* getClientByID(uint16_t conn_id);
static NimBLEClient* getClientByPeerAddress(const NimBLEAddress &peer_addr);
@ -181,8 +181,6 @@ private:
static NimBLESecurityCallbacks* m_securityCallbacks;
static uint32_t m_passkey;
static ble_gap_event_listener m_listener;
public:
static gap_event_handler m_customGapHandler;
};

View File

@ -24,7 +24,9 @@
static const char LOG_TAG[] = "NimBLEEddystoneTLM";
/**
* @brief Construct a default EddystoneTLM beacon object.
*/
NimBLEEddystoneTLM::NimBLEEddystoneTLM() {
beaconUUID = 0xFEAA;
m_eddystoneData.frameType = EDDYSTONE_TLM_FRAME_TYPE;
@ -35,34 +37,73 @@ NimBLEEddystoneTLM::NimBLEEddystoneTLM() {
m_eddystoneData.tmil = 0;
} // NimBLEEddystoneTLM
/**
* @brief Retrieve the data that is being advertised.
* @return The advertised data.
*/
std::string NimBLEEddystoneTLM::getData() {
return std::string((char*) &m_eddystoneData, sizeof(m_eddystoneData));
} // getData
/**
* @brief Get the UUID being advertised.
* @return The UUID advertised.
*/
NimBLEUUID NimBLEEddystoneTLM::getUUID() {
return NimBLEUUID(beaconUUID);
} // getUUID
/**
* @brief Get the version being advertised.
* @return The version number.
*/
uint8_t NimBLEEddystoneTLM::getVersion() {
return m_eddystoneData.version;
} // getVersion
/**
* @brief Get the battery voltage.
* @return The battery voltage.
*/
uint16_t NimBLEEddystoneTLM::getVolt() {
return ENDIAN_CHANGE_U16(m_eddystoneData.volt);
} // getVolt
/**
* @brief Get the temperature being advertised.
* @return The temperature value.
*/
float NimBLEEddystoneTLM::getTemp() {
return ENDIAN_CHANGE_U16(m_eddystoneData.temp) / 256.0f;
} // getTemp
/**
* @brief Get the count of advertisments sent.
* @return The number of advertisments.
*/
uint32_t NimBLEEddystoneTLM::getCount() {
return ENDIAN_CHANGE_U32(m_eddystoneData.advCount);
} // getCount
/**
* @brief Get the advertisment time.
* @return The advertisment time.
*/
uint32_t NimBLEEddystoneTLM::getTime() {
return (ENDIAN_CHANGE_U32(m_eddystoneData.tmil)) / 10;
} // getTime
/**
* @brief Get a string representation of the beacon.
* @return The string representation.
*/
std::string NimBLEEddystoneTLM::toString() {
std::string out = "";
uint32_t rawsec = ENDIAN_CHANGE_U32(m_eddystoneData.tmil);
@ -113,8 +154,10 @@ std::string NimBLEEddystoneTLM::toString() {
return out;
} // toString
/**
* Set the raw data for the beacon record.
* @brief Set the raw data for the beacon advertisment.
* @param [in] data The raw data to advertise.
*/
void NimBLEEddystoneTLM::setData(const std::string &data) {
if (data.length() != sizeof(m_eddystoneData)) {
@ -125,26 +168,56 @@ void NimBLEEddystoneTLM::setData(const std::string &data) {
memcpy(&m_eddystoneData, data.data(), data.length());
} // setData
/**
* @brief Set the UUID to advertise.
* @param [in] l_uuid The UUID.
*/
void NimBLEEddystoneTLM::setUUID(const NimBLEUUID &l_uuid) {
beaconUUID = l_uuid.getNative()->u16.value;
} // setUUID
/**
* @brief Set the version to advertise.
* @param [in] version The version number.
*/
void NimBLEEddystoneTLM::setVersion(uint8_t version) {
m_eddystoneData.version = version;
} // setVersion
/**
* @brief Set the battery voltage to advertise.
* @param [in] volt The voltage in millivolts.
*/
void NimBLEEddystoneTLM::setVolt(uint16_t volt) {
m_eddystoneData.volt = volt;
} // setVolt
/**
* @brief Set the temperature to advertise.
* @param [in] temp The temperature value.
*/
void NimBLEEddystoneTLM::setTemp(float temp) {
m_eddystoneData.temp = (uint16_t)temp;
} // setTemp
/**
* @brief Set the advertisment count.
* @param [in] advCount The advertisment number.
*/
void NimBLEEddystoneTLM::setCount(uint32_t advCount) {
m_eddystoneData.advCount = advCount;
} // setCount
/**
* @brief Set the advertisment time.
* @param [in] tmil The advertisment time in milliseconds.
*/
void NimBLEEddystoneTLM::setTime(uint32_t tmil) {
m_eddystoneData.tmil = tmil;
} // setTime

View File

@ -21,6 +21,10 @@
static const char LOG_TAG[] = "NimBLEEddystoneURL";
/**
* @brief Construct a default EddystoneURL beacon object.
*/
NimBLEEddystoneURL::NimBLEEddystoneURL() {
beaconUUID = 0xFEAA;
lengthURL = 0;
@ -29,22 +33,47 @@ NimBLEEddystoneURL::NimBLEEddystoneURL() {
memset(m_eddystoneData.url, 0, sizeof(m_eddystoneData.url));
} // BLEEddystoneURL
/**
* @brief Retrieve the data that is being advertised.
* @return The advertised data.
*/
std::string NimBLEEddystoneURL::getData() {
return std::string((char*) &m_eddystoneData, sizeof(m_eddystoneData));
} // getData
/**
* @brief Get the UUID being advertised.
* @return The UUID advertised.
*/
NimBLEUUID NimBLEEddystoneURL::getUUID() {
return NimBLEUUID(beaconUUID);
} // getUUID
/**
* @brief Get the transmit power being advertised.
* @return The transmit power.
*/
int8_t NimBLEEddystoneURL::getPower() {
return m_eddystoneData.advertisedTxPower;
} // getPower
/**
* @brief Get the raw URL being advertised.
* @return The raw URL.
*/
std::string NimBLEEddystoneURL::getURL() {
return std::string((char*) &m_eddystoneData.url, sizeof(m_eddystoneData.url));
} // getURL
/**
* @brief Get the full URL being advertised.
* @return The full URL.
*/
std::string NimBLEEddystoneURL::getDecodedURL() {
std::string decodedURL = "";
@ -123,7 +152,8 @@ std::string NimBLEEddystoneURL::getDecodedURL() {
/**
* Set the raw data for the beacon record.
* @brief Set the raw data for the beacon advertisment.
* @param [in] data The raw data to advertise.
*/
void NimBLEEddystoneURL::setData(const std::string &data) {
if (data.length() > sizeof(m_eddystoneData)) {
@ -136,14 +166,29 @@ void NimBLEEddystoneURL::setData(const std::string &data) {
lengthURL = data.length() - (sizeof(m_eddystoneData) - sizeof(m_eddystoneData.url));
} // setData
/**
* @brief Set the UUID to advertise.
* @param [in] l_uuid The UUID.
*/
void NimBLEEddystoneURL::setUUID(const NimBLEUUID &l_uuid) {
beaconUUID = l_uuid.getNative()->u16.value;
} // setUUID
/**
* @brief Set the transmit power to advertise.
* @param [in] advertisedTxPower The transmit power level.
*/
void NimBLEEddystoneURL::setPower(int8_t advertisedTxPower) {
m_eddystoneData.advertisedTxPower = advertisedTxPower;
} // setPower
/**
* @brief Set the URL to advertise.
* @param [in] url The URL.
*/
void NimBLEEddystoneURL::setURL(const std::string &url) {
if (url.length() > sizeof(m_eddystoneData.url)) {
NIMBLE_LOGE(LOG_TAG, "Unable to set the url ... length passed in was %d and max expected %d",

View File

@ -260,13 +260,12 @@ NimBLERemoteDescriptor* NimBLERemoteCharacteristic::getDescriptor(const NimBLEUU
/**
* @Get a pointer to the vector of found descriptors.
* @param [in] bool value to indicate if the current vector should be cleared and
* subsequently all descriptors for this characteristic retrieved from the peripheral.
* If false the vector will be returned with the currently stored descriptors,
* if the vector is empty it will retrieve all descriptors for this characteristic
* from the peripheral.
* @return a pointer to the vector of descriptors for this characteristic.
* @brief Get a pointer to the vector of found descriptors.
* @param [in] refresh If true the current descriptor vector will be cleared and\n
* all descriptors for this characteristic retrieved from the peripheral.\n
* If false the vector will be returned with the currently stored descriptors
* of this characteristic.
* @return A pointer to the vector of descriptors for this characteristic.
*/
std::vector<NimBLERemoteDescriptor*>* NimBLERemoteCharacteristic::getDescriptors(bool refresh) {
if(refresh) {
@ -338,6 +337,7 @@ NimBLEUUID NimBLERemoteCharacteristic::getUUID() {
/**
* @brief Get the value of the remote characteristic.
* @param [in] timestamp A pointer to a time_t struct to store the time the value was read.
* @return The value of the remote characteristic.
*/
std::string NimBLERemoteCharacteristic::getValue(time_t *timestamp) {
@ -355,6 +355,7 @@ std::string NimBLERemoteCharacteristic::getValue(time_t *timestamp) {
/**
* @brief Read an unsigned 16 bit value
* @return The unsigned 16 bit value.
* @deprecated Use readValue<uint16_t>().
*/
uint16_t NimBLERemoteCharacteristic::readUInt16() {
return readValue<uint16_t>();
@ -364,6 +365,7 @@ uint16_t NimBLERemoteCharacteristic::readUInt16() {
/**
* @brief Read an unsigned 32 bit value.
* @return the unsigned 32 bit value.
* @deprecated Use readValue<uint32_t>().
*/
uint32_t NimBLERemoteCharacteristic::readUInt32() {
return readValue<uint32_t>();
@ -373,6 +375,7 @@ uint32_t NimBLERemoteCharacteristic::readUInt32() {
/**
* @brief Read a byte value
* @return The value as a byte
* @deprecated Use readValue<uint8_t>().
*/
uint8_t NimBLERemoteCharacteristic::readUInt8() {
return readValue<uint8_t>();
@ -390,6 +393,7 @@ float NimBLERemoteCharacteristic::readFloat() {
/**
* @brief Read the value of the remote characteristic.
* @param [in] timestamp A pointer to a time_t struct to store the time the value was read.
* @return The value of the remote characteristic.
*/
std::string NimBLERemoteCharacteristic::readValue(time_t *timestamp) {
@ -458,7 +462,7 @@ std::string NimBLERemoteCharacteristic::readValue(time_t *timestamp) {
/**
* @brief Callback for characteristic read operation.
* @return 0 or error code.
* @return success == 0 or error code.
*/
int NimBLERemoteCharacteristic::onReadCB(uint16_t conn_handle,
const struct ble_gatt_error *error,
@ -498,12 +502,13 @@ int NimBLERemoteCharacteristic::onReadCB(uint16_t conn_handle,
/**
* @brief Subscribe or unsubscribe for notifications or indications.
* @param [in] uint16_t val 0x00 to unsubscribe, 0x01 for notifications, 0x02 for indications.
* @param [in] notifyCallback A callback to be invoked for a notification. If NULL is provided then no callback
* is performed for notifications.
* @param [in] val 0x00 to unsubscribe, 0x01 for notifications, 0x02 for indications.
* @param [in] notifyCallback A callback to be invoked for a notification.
* @param [in] response If write response required set this to true.
* If NULL is provided then no callback is performed.
* @return true if successful.
*/
bool NimBLERemoteCharacteristic::setNotify(uint16_t val, bool response, notify_callback notifyCallback) {
bool NimBLERemoteCharacteristic::setNotify(uint16_t val, notify_callback notifyCallback, bool response) {
NIMBLE_LOGD(LOG_TAG, ">> setNotify(): %s, %02x", toString().c_str(), val);
NimBLERemoteDescriptor* desc = getDescriptor(NimBLEUUID((uint16_t)0x2902));
@ -522,43 +527,44 @@ bool NimBLERemoteCharacteristic::setNotify(uint16_t val, bool response, notify_c
/**
* @brief Subscribe for notifications or indications.
* @param [in] bool if true, subscribe for notifications, false subscribe for indications.
* @param [in] bool if true, require a write response from the descriptor write operation.
* @param [in] notifyCallback A callback to be invoked for a notification. If NULL is provided then no callback
* is performed for notifications.
* @param [in] notifications If true, subscribe for notifications, false subscribe for indications.
* @param [in] notifyCallback A callback to be invoked for a notification.
* @param [in] response If true, require a write response from the descriptor write operation.
* If NULL is provided then no callback is performed.
* @return true if successful.
*/
bool NimBLERemoteCharacteristic::subscribe(bool notifications, bool response, notify_callback notifyCallback) {
bool NimBLERemoteCharacteristic::subscribe(bool notifications, notify_callback notifyCallback, bool response) {
if(notifications) {
return setNotify(0x01, response, notifyCallback);
return setNotify(0x01, notifyCallback, response);
} else {
return setNotify(0x02, response, notifyCallback);
return setNotify(0x02, notifyCallback, response);
}
} // subscribe
/**
* @brief Unsubscribe for notifications or indications.
* @param [in] bool if true, require a write response from the descriptor write operation.
* @param [in] response bool if true, require a write response from the descriptor write operation.
* @return true if successful.
*/
bool NimBLERemoteCharacteristic::unsubscribe(bool response) {
return setNotify(0x00, response);
return setNotify(0x00, nullptr, response);
} // unsubscribe
/**
* @brief backward-compatibility method for subscribe/unsubscribe notifications/indications
* @param [in] notifyCallback A callback to be invoked for a notification. If NULL is provided then we are
* unregistering for notifications.
* @param [in] bool if true, register for notifications, false register for indications.
* @param [in] bool if true, require a write response from the descriptor write operation.
* @param [in] notifyCallback A callback to be invoked for a notification. If NULL is provided then we
* will unregister for notifications.
* @param [in] notifications If true, register for notifications, false register for indications.
* @param [in] response If true, require a write response from the descriptor write operation.
* @return true if successful.
* @deprecated Use subscribe() / unsubscribe() instead.
*/
bool NimBLERemoteCharacteristic::registerForNotify(notify_callback notifyCallback, bool notifications, bool response) {
bool success;
if(notifyCallback != nullptr) {
success = subscribe(notifications, response, notifyCallback);
success = subscribe(notifications, notifyCallback, response);
} else {
success = unsubscribe(response);
}
@ -568,10 +574,9 @@ bool NimBLERemoteCharacteristic::registerForNotify(notify_callback notifyCallbac
/**
* @brief Delete the descriptors in the descriptor vector.
* We maintain a vector called m_descriptorVector that contains pointers to BLERemoteDescriptors
* @details We maintain a vector called m_descriptorVector that contains pointers to NimBLERemoteDescriptors
* object references. Since we allocated these in this class, we are also responsible for deleting
* them. This method does just that.
* @return N/A.
*/
void NimBLERemoteCharacteristic::deleteDescriptors() {
NIMBLE_LOGD(LOG_TAG, ">> deleteDescriptors");
@ -587,7 +592,7 @@ void NimBLERemoteCharacteristic::deleteDescriptors() {
/**
* @brief Delete descriptor by UUID
* @param [in] uuid The UUID of the descriptor to be deleted.
* @return Number of services left.
* @return Number of descriptors left in the vector.
*/
size_t NimBLERemoteCharacteristic::deleteDescriptor(const NimBLEUUID &uuid) {
NIMBLE_LOGD(LOG_TAG, ">> deleteDescriptor");
@ -607,7 +612,7 @@ size_t NimBLERemoteCharacteristic::deleteDescriptor(const NimBLEUUID &uuid) {
/**
* @brief Convert a BLERemoteCharacteristic to a string representation;
* @brief Convert a NimBLERemoteCharacteristic to a string representation;
* @return a String representation.
*/
std::string NimBLERemoteCharacteristic::toString() {
@ -725,7 +730,7 @@ bool NimBLERemoteCharacteristic::writeValue(const uint8_t* data, size_t length,
/**
* @brief Callback for characteristic write operation.
* @return 0 or error code.
* @return success == 0 or error code.
*/
int NimBLERemoteCharacteristic::onWriteCB(uint16_t conn_handle,
const struct ble_gatt_error *error,

View File

@ -24,13 +24,14 @@
#include "NimBLERemoteDescriptor.h"
#include <vector>
#include <functional>
class NimBLERemoteService;
class NimBLERemoteDescriptor;
typedef void (*notify_callback)(NimBLERemoteCharacteristic* pBLERemoteCharacteristic,
uint8_t* pData, size_t length, bool isNotify);
typedef std::function<void (NimBLERemoteCharacteristic* pBLERemoteCharacteristic,
uint8_t* pData, size_t length, bool isNotify)> notify_callback;
typedef struct {
const NimBLEUUID *uuid;
@ -63,6 +64,15 @@ public:
NimBLEUUID getUUID();
std::string readValue(time_t *timestamp = nullptr);
/**
* @brief A template to convert the remote characteristic data to <type\>.
* @tparam T The type to convert the data to.
* @param [in] timestamp A pointer to a time_t struct to store the time the value was read.
* @param [in] skipSizeCheck If true it will skip checking if the data size is less than <tt>sizeof(<type\>)</tt>.
* @return The data converted to <type\> or NULL if skipSizeCheck is false and the data is
* less than <tt>sizeof(<type\>)</tt>.
* @details <b>Use:</b> <tt>readValue<type>(&timestamp, skipSizeCheck);</tt>
*/
template<typename T>
T readValue(time_t *timestamp = nullptr, bool skipSizeCheck = false) {
std::string value = readValue(timestamp);
@ -77,6 +87,15 @@ public:
float readFloat() __attribute__ ((deprecated("Use template readValue<float>()")));
std::string getValue(time_t *timestamp = nullptr);
/**
* @brief A template to convert the remote characteristic data to <type\>.
* @tparam T The type to convert the data to.
* @param [in] timestamp A pointer to a time_t struct to store the time the value was read.
* @param [in] skipSizeCheck If true it will skip checking if the data size is less than <tt>sizeof(<type\>)</tt>.
* @return The data converted to <type\> or NULL if skipSizeCheck is false and the data is
* less than <tt>sizeof(<type\>)</tt>.
* @details <b>Use:</b> <tt>getValue<type>(&timestamp, skipSizeCheck);</tt>
*/
template<typename T>
T getValue(time_t *timestamp = nullptr, bool skipSizeCheck = false) {
std::string value = getValue(timestamp);
@ -86,9 +105,9 @@ public:
}
bool subscribe(bool notifications = true,
bool response = true,
notify_callback notifyCallback = nullptr);
bool unsubscribe(bool response = true);
notify_callback notifyCallback = nullptr,
bool response = false);
bool unsubscribe(bool response = false);
bool registerForNotify(notify_callback notifyCallback,
bool notifications = true,
bool response = true)
@ -98,6 +117,11 @@ public:
bool response = false);
bool writeValue(const std::string &newValue,
bool response = false);
/**
* @brief Convenience template to set the remote characteristic value to <type\>val.
* @param [in] s The value to write.
* @param [in] response True == request write response.
*/
template<typename T>
bool writeValue(const T &s, bool response = false) {
return writeValue((uint8_t*)&s, sizeof(T), response);
@ -115,7 +139,7 @@ private:
friend class NimBLERemoteDescriptor;
// Private member functions
bool setNotify(uint16_t val, bool response = true, notify_callback notifyCallback = nullptr);
bool setNotify(uint16_t val, notify_callback notifyCallback = nullptr, bool response = true);
bool retrieveDescriptors(const NimBLEUUID *uuid_filter = nullptr);
static int onReadCB(uint16_t conn_handle, const struct ble_gatt_error *error,
struct ble_gatt_attr *attr, void *arg);

View File

@ -25,8 +25,8 @@ static const char* LOG_TAG = "NimBLERemoteDescriptor";
/**
* @brief Remote descriptor constructor.
* @param [in] Reference to the Characteristic that this belongs to.
* @param [in] Reference to the struct that contains the descriptor information.
* @param [in] pRemoteCharacteristic A pointer to the Characteristic that this belongs to.
* @param [in] dsc A pointer to the struct that contains the descriptor information.
*/
NimBLERemoteDescriptor::NimBLERemoteDescriptor(NimBLERemoteCharacteristic* pRemoteCharacteristic,
const struct ble_gatt_dsc *dsc)
@ -78,6 +78,11 @@ NimBLEUUID NimBLERemoteDescriptor::getUUID() {
} // getUUID
/**
* @brief Read a byte value
* @return The value as a byte
* @deprecated Use readValue<uint8_t>().
*/
uint8_t NimBLERemoteDescriptor::readUInt8() {
std::string value = readValue();
if (value.length() >= 1) {
@ -87,6 +92,11 @@ uint8_t NimBLERemoteDescriptor::readUInt8() {
} // readUInt8
/**
* @brief Read an unsigned 16 bit value
* @return The unsigned 16 bit value.
* @deprecated Use readValue<uint16_t>().
*/
uint16_t NimBLERemoteDescriptor::readUInt16() {
std::string value = readValue();
if (value.length() >= 2) {
@ -96,6 +106,11 @@ uint16_t NimBLERemoteDescriptor::readUInt16() {
} // readUInt16
/**
* @brief Read an unsigned 32 bit value.
* @return the unsigned 32 bit value.
* @deprecated Use readValue<uint32_t>().
*/
uint32_t NimBLERemoteDescriptor::readUInt32() {
std::string value = readValue();
if (value.length() >= 4) {
@ -105,6 +120,10 @@ uint32_t NimBLERemoteDescriptor::readUInt32() {
} // readUInt32
/**
* @brief Read the value of the remote descriptor.
* @return The value of the remote descriptor.
*/
std::string NimBLERemoteDescriptor::readValue() {
NIMBLE_LOGD(LOG_TAG, ">> Descriptor readValue: %s", toString().c_str());
@ -161,7 +180,7 @@ std::string NimBLERemoteDescriptor::readValue() {
/**
* @brief Callback for Descriptor read operation.
* @return 0 or error code.
* @return success == 0 or error code.
*/
int NimBLERemoteDescriptor::onReadCB(uint16_t conn_handle,
const struct ble_gatt_error *error,
@ -200,8 +219,8 @@ int NimBLERemoteDescriptor::onReadCB(uint16_t conn_handle,
/**
* @brief Return a string representation of this BLE Remote Descriptor.
* @retun A string representation of this BLE Remote Descriptor.
* @brief Return a string representation of this Remote Descriptor.
* @return A string representation of this Remote Descriptor.
*/
std::string NimBLERemoteDescriptor::toString() {
std::string res = "Descriptor: uuid: " + getUUID().toString();
@ -216,7 +235,7 @@ std::string NimBLERemoteDescriptor::toString() {
/**
* @brief Callback for descriptor write operation.
* @return 0 or error code.
* @return success == 0 or error code.
*/
int NimBLERemoteDescriptor::onWriteCB(uint16_t conn_handle,
const struct ble_gatt_error *error,
@ -242,7 +261,8 @@ int NimBLERemoteDescriptor::onWriteCB(uint16_t conn_handle,
* @brief Write data to the BLE Remote Descriptor.
* @param [in] data The data to send to the remote descriptor.
* @param [in] length The length of the data to send.
* @param [in] response True if we expect a response.
* @param [in] response True if we expect a write response.
* @return True if successful
*/
bool NimBLERemoteDescriptor::writeValue(const uint8_t* data, size_t length, bool response) {
@ -322,11 +342,11 @@ bool NimBLERemoteDescriptor::writeValue(const uint8_t* data, size_t length, bool
* @brief Write data represented as a string to the BLE Remote Descriptor.
* @param [in] newValue The data to send to the remote descriptor.
* @param [in] response True if we expect a response.
* @return True if successful
*/
bool NimBLERemoteDescriptor::writeValue(const std::string &newValue, bool response) {
return writeValue((uint8_t*) newValue.data(), newValue.length(), response);
} // writeValue
#endif // #if defined( CONFIG_BT_NIMBLE_ROLE_CENTRAL)
#endif /* CONFIG_BT_ENABLED */

View File

@ -33,6 +33,14 @@ public:
NimBLEUUID getUUID();
std::string readValue();
/**
* @brief A template to convert the remote descriptor data to <type\>.
* @tparam T The type to convert the data to.
* @param [in] skipSizeCheck If true it will skip checking if the data size is less than <tt>sizeof(<type\>)</tt>.
* @return The data converted to <type\> or NULL if skipSizeCheck is false and the data is
* less than <tt>sizeof(<type\>)</tt>.
* @details <b>Use:</b> <tt>readValue<type>(skipSizeCheck);</tt>
*/
template<typename T>
T readValue(bool skipSizeCheck = false) {
std::string value = readValue();
@ -41,12 +49,18 @@ public:
return *((T *)pData);
}
uint8_t readUInt8() __attribute__ ((deprecated));
uint16_t readUInt16() __attribute__ ((deprecated));
uint32_t readUInt32() __attribute__ ((deprecated));
uint8_t readUInt8() __attribute__ ((deprecated("Use template readValue<uint8_t>()")));
uint16_t readUInt16() __attribute__ ((deprecated("Use template readValue<uint16_t>()")));
uint32_t readUInt32() __attribute__ ((deprecated("Use template readValue<uint32_t>()")));
std::string toString(void);
bool writeValue(const uint8_t* data, size_t length, bool response = false);
bool writeValue(const std::string &newValue, bool response = false);
/**
* @brief Convenience template to set the remote descriptor value to <type\>val.
* @param [in] s The value to write.
* @param [in] response True == request write response.
*/
template<typename T>
bool writeValue(const T &s, bool response = false) {
return writeValue((uint8_t*)&s, sizeof(T), response);

View File

@ -26,8 +26,8 @@ static const char* LOG_TAG = "NimBLERemoteService";
/**
* @brief Remote Service constructor.
* @param [in] Reference to the client this belongs to.
* @param [in] Refernce to the structure with the services' information.
* @param [in] pClient A pointer to the client this belongs to.
* @param [in] service A pointer to the structure with the service information.
*/
NimBLERemoteService::NimBLERemoteService(NimBLEClient* pClient, const struct ble_gatt_svc* service) {
@ -55,7 +55,6 @@ NimBLERemoteService::NimBLERemoteService(NimBLEClient* pClient, const struct ble
/**
* @brief When deleting the service make sure we delete all characteristics and descriptors.
* Also release any semaphores they may be holding.
*/
NimBLERemoteService::~NimBLERemoteService() {
deleteCharacteristics();
@ -83,7 +82,7 @@ std::vector<NimBLERemoteCharacteristic*>::iterator NimBLERemoteService::end() {
/**
* @brief Get the remote characteristic object for the characteristic UUID.
* @param [in] uuid Remote characteristic uuid.
* @return Reference to the remote characteristic object.
* @return A pointer to the remote characteristic object.
*/
NimBLERemoteCharacteristic* NimBLERemoteService::getCharacteristic(const char* uuid) {
return getCharacteristic(NimBLEUUID(uuid));
@ -93,7 +92,7 @@ NimBLERemoteCharacteristic* NimBLERemoteService::getCharacteristic(const char* u
/**
* @brief Get the characteristic object for the UUID.
* @param [in] uuid Characteristic uuid.
* @return Reference to the characteristic object, or nullptr if not found.
* @return A pointer to the characteristic object, or nullptr if not found.
*/
NimBLERemoteCharacteristic* NimBLERemoteService::getCharacteristic(const NimBLEUUID &uuid) {
for(auto &it: m_characteristicVector) {
@ -114,15 +113,12 @@ NimBLERemoteCharacteristic* NimBLERemoteService::getCharacteristic(const NimBLEU
/**
* @Get a pointer to the vector of found characteristics.
* @param [in] bool value to indicate if the current vector should be cleared and
* subsequently all characteristics for this service retrieved from the peripheral.
* If false the vector will be returned with the currently stored characteristics,
* If true it will retrieve all characteristics of this service from the peripheral
* and return the vector with all characteristics for this service.
* @return a pointer to the vector of descriptors for this characteristic.
* @brief Get a pointer to the vector of found characteristics.
* @param [in] refresh If true the current characteristics vector will cleared and
* all characteristics for this service retrieved from the peripheral.
* If false the vector will be returned with the currently stored characteristics of this service.
* @return A pointer to the vector of descriptors for this characteristic.
*/
std::vector<NimBLERemoteCharacteristic*>* NimBLERemoteService::getCharacteristics(bool refresh) {
if(refresh) {
deleteCharacteristics();
@ -140,6 +136,7 @@ std::vector<NimBLERemoteCharacteristic*>* NimBLERemoteService::getCharacteristic
/**
* @brief Callback for Characterisic discovery.
* @return success == 0 or error code.
*/
int NimBLERemoteService::characteristicDiscCB(uint16_t conn_handle,
const struct ble_gatt_error *error,
@ -182,7 +179,7 @@ int NimBLERemoteService::characteristicDiscCB(uint16_t conn_handle,
/**
* @brief Retrieve all the characteristics for this service.
* This function will not return until we have all the characteristics.
* @return N/A
* @return True if successful.
*/
bool NimBLERemoteService::retrieveCharacteristics(const NimBLEUUID *uuid_filter) {
NIMBLE_LOGD(LOG_TAG, ">> retrieveCharacteristics() for service: %s", getUUID().toString().c_str());
@ -299,10 +296,9 @@ bool NimBLERemoteService::setValue(const NimBLEUUID &characteristicUuid, const s
/**
* @brief Delete the characteristics in the characteristics vector.
* We maintain a vector called m_characteristicsVector that contains pointers to BLERemoteCharacteristic
* @details We maintain a vector called m_characteristicsVector that contains pointers to BLERemoteCharacteristic
* object references. Since we allocated these in this class, we are also responsible for deleting
* them. This method does just that.
* @return N/A.
*/
void NimBLERemoteService::deleteCharacteristics() {
NIMBLE_LOGD(LOG_TAG, ">> deleteCharacteristics");
@ -316,7 +312,7 @@ void NimBLERemoteService::deleteCharacteristics() {
/**
* @brief Delete characteristic by UUID
* @param [in] uuid The UUID of the characteristic to be cleared.
* @param [in] uuid The UUID of the characteristic to be removed from the local database.
* @return Number of characteristics left.
*/
size_t NimBLERemoteService::deleteCharacteristic(const NimBLEUUID &uuid) {

View File

@ -45,7 +45,7 @@ public:
void deleteCharacteristics();
size_t deleteCharacteristic(const NimBLEUUID &uuid);
NimBLEClient* getClient(void);
uint16_t getHandle();
//uint16_t getHandle();
NimBLEUUID getUUID(void);
std::string getValue(const NimBLEUUID &characteristicUuid);
bool setValue(const NimBLEUUID &characteristicUuid,

View File

@ -44,6 +44,13 @@ NimBLEScan::NimBLEScan() {
}
/**
* @brief Scan destructor, release any allocated resources.
*/
NimBLEScan::~NimBLEScan() {
clearResults();
}
/**
* @brief Handle GAP events related to scans.
* @param [in] event The event type for this event.
@ -52,8 +59,6 @@ NimBLEScan::NimBLEScan() {
/*STATIC*/int NimBLEScan::handleGapEvent(ble_gap_event* event, void* arg) {
NimBLEScan* pScan = (NimBLEScan*)arg;
struct ble_hs_adv_fields fields;
int rc = 0;
switch(event->type) {
@ -63,13 +68,6 @@ NimBLEScan::NimBLEScan() {
return 0;
}
rc = ble_hs_adv_parse_fields(&fields, event->disc.data,
event->disc.length_data);
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "Gap Event Parse ERROR.");
return 0;
}
NimBLEAddress advertisedAddress(event->disc.addr);
// Examine our list of ignored addresses and stop processing if we don't want to see it or are already connected
@ -92,7 +90,6 @@ NimBLEScan::NimBLEScan() {
// Otherwise just update the relevant parameters of the already known device.
if(advertisedDevice == nullptr){
advertisedDevice = new NimBLEAdvertisedDevice();
advertisedDevice->setAddressType(event->disc.addr.type);
advertisedDevice->setAddress(advertisedAddress);
advertisedDevice->setAdvType(event->disc.event_type);
pScan->m_scanResults.m_advertisedDevicesVector.push_back(advertisedDevice);
@ -102,9 +99,9 @@ NimBLEScan::NimBLEScan() {
NIMBLE_LOGI(LOG_TAG, "UPDATING PREVIOUSLY FOUND DEVICE: %s", advertisedAddress.toString().c_str());
}
advertisedDevice->setRSSI(event->disc.rssi);
advertisedDevice->parseAdvertisement(&fields);
advertisedDevice->setScan(pScan);
advertisedDevice->setAdvertisementResult(event->disc.data, event->disc.length_data);
if(event->disc.length_data > 0) {
advertisedDevice->parseAdvertisement(event->disc.data, event->disc.length_data);
}
advertisedDevice->m_timestamp = time(nullptr);
if (pScan->m_pAdvertisedDeviceCallbacks) {
@ -151,7 +148,6 @@ NimBLEScan::NimBLEScan() {
* @brief Should we perform an active or passive scan?
* The default is a passive scan. An active scan means that we will wish a scan response.
* @param [in] active If true, we perform an active scan otherwise a passive scan.
* @return N/A.
*/
void NimBLEScan::setActiveScan(bool active) {
if (active) {
@ -165,40 +161,40 @@ void NimBLEScan::setActiveScan(bool active) {
/**
* @brief Set whether or not the BLE controller should only report results
* from devices it has not already seen.
* @param [in] active If true, scanned devices will only be reported once.
* @param [in] enabled If true, scanned devices will only be reported once.
* @details The controller has a limited buffer and will start reporting
* dupicate devices once the limit is reached.
*/
void NimBLEScan::setDuplicateFilter(bool active) {
m_scan_params.filter_duplicates = active;
void NimBLEScan::setDuplicateFilter(bool enabled) {
m_scan_params.filter_duplicates = enabled;
} // setDuplicateFilter
/**
* @brief Set whether or not the BLE controller only report scan results
* from devices advertising in limited discovery mode, i.e. directed advertising.
* @param [in] active If true, only limited discovery devices will be in scan results.
* @param [in] enabled If true, only limited discovery devices will be in scan results.
*/
void NimBLEScan::setLimitedOnly(bool active) {
m_scan_params.limited = active;
void NimBLEScan::setLimitedOnly(bool enabled) {
m_scan_params.limited = enabled;
} // setLimited
/**
* @brief Sets the scan filter policy.
* @param [in] filter Can be one of:
* BLE_HCI_SCAN_FILT_NO_WL (0)
* Scanner processes all advertising packets (white list not used) except
* * BLE_HCI_SCAN_FILT_NO_WL (0)
* Scanner processes all advertising packets (white list not used) except\n
* directed, connectable advertising packets not sent to the scanner.
* BLE_HCI_SCAN_FILT_USE_WL (1)
* Scanner processes advertisements from white list only. A connectable,
* * BLE_HCI_SCAN_FILT_USE_WL (1)
* Scanner processes advertisements from white list only. A connectable,\n
* directed advertisment is ignored unless it contains scanners address.
* BLE_HCI_SCAN_FILT_NO_WL_INITA (2)
* Scanner process all advertising packets (white list not used). A
* * BLE_HCI_SCAN_FILT_NO_WL_INITA (2)
* Scanner process all advertising packets (white list not used). A\n
* connectable, directed advertisement shall not be ignored if the InitA
* is a resolvable private address.
* BLE_HCI_SCAN_FILT_USE_WL_INITA (3)
* Scanner process advertisements from white list only. A connectable,
* * BLE_HCI_SCAN_FILT_USE_WL_INITA (3)
* Scanner process advertisements from white list only. A connectable,\n
* directed advertisement shall not be ignored if the InitA is a
* resolvable private address.
*/
@ -221,7 +217,7 @@ void NimBLEScan::setAdvertisedDeviceCallbacks(NimBLEAdvertisedDeviceCallbacks* p
/**
* @brief Set the interval to scan.
* @param [in] The interval in msecs.
* @param [in] intervalMSecs The scan interval (how often) in milliseconds.
*/
void NimBLEScan::setInterval(uint16_t intervalMSecs) {
m_scan_params.itvl = intervalMSecs / 0.625;
@ -237,11 +233,20 @@ void NimBLEScan::setWindow(uint16_t windowMSecs) {
} // setWindow
/**
* @brief Get the status of the scanner.
* @return true if scanning or scan starting.
*/
bool NimBLEScan::isScanning() {
return !m_stopped;
}
/**
* @brief Start scanning.
* @param [in] duration The duration in seconds for which to scan.
* @param [in] scanCompleteCB A function to be called when scanning has completed.
* @param [in] are we continue scan (true) or we want to clear stored devices (false)
* @param [in] is_continue Set to true to save previous scan results, false to clear them.
* @return True if scan started or false if there was an error.
*/
bool NimBLEScan::start(uint32_t duration, void (*scanCompleteCB)(NimBLEScanResults), bool is_continue) {
@ -309,7 +314,8 @@ bool NimBLEScan::start(uint32_t duration, void (*scanCompleteCB)(NimBLEScanResul
/**
* @brief Start scanning and block until scanning has been completed.
* @param [in] duration The duration in seconds for which to scan.
* @return The BLEScanResults.
* @param [in] is_continue Set to true to save previous scan results, false to clear them.
* @return The NimBLEScanResults.
*/
NimBLEScanResults NimBLEScan::start(uint32_t duration, bool is_continue) {
if(duration == 0) {
@ -330,7 +336,7 @@ NimBLEScanResults NimBLEScan::start(uint32_t duration, bool is_continue) {
/**
* @brief Stop an in progress scan.
* @return N/A.
* @return True if successful.
*/
bool NimBLEScan::stop() {
NIMBLE_LOGD(LOG_TAG, ">> stop()");
@ -343,7 +349,7 @@ bool NimBLEScan::stop() {
m_stopped = true;
if (m_scanCompleteCB != nullptr) {
if (rc != BLE_HS_EALREADY && m_scanCompleteCB != nullptr) {
m_scanCompleteCB(m_scanResults);
}
@ -356,7 +362,11 @@ bool NimBLEScan::stop() {
} // stop
// delete peer device from cache after disconnecting, it is required in case we are connecting to devices with not public address
/**
* @brief Delete peer device from the scan results vector.
* @param [in] address The address of the device to delete from the results.
* @details After disconnecting, it may be required in the case we were connected to a device without a public address.
*/
void NimBLEScan::erase(const NimBLEAddress &address) {
NIMBLE_LOGI(LOG_TAG, "erase device: %s", address.toString().c_str());
@ -371,8 +381,7 @@ void NimBLEScan::erase(const NimBLEAddress &address) {
/**
* @brief If the host reset the scan will have stopped so we should flag it and release the semaphore.
* @return N/A.
* @brief If the host reset the scan will have stopped so we should set the flag as stopped.
*/
void NimBLEScan::onHostReset() {
m_stopped = true;
@ -411,7 +420,7 @@ void NimBLEScanResults::dump() {
/**
* @brief Return the count of devices found in the last scan.
* @brief Get the count of devices found in the last scan.
* @return The number of devices found in the last scan.
*/
int NimBLEScanResults::getCount() {
@ -449,7 +458,7 @@ std::vector<NimBLEAdvertisedDevice*>::iterator NimBLEScanResults::end() {
/**
* @brief Return a pointer to the specified device at the given address.
* @brief Get a pointer to the specified device at the given address.
* If the address is not found a nullptr is returned.
* @param [in] address The address of the device.
* @return A pointer to the device at the specified address.

View File

@ -33,9 +33,9 @@ class NimBLEAdvertisedDeviceCallbacks;
class NimBLEAddress;
/**
* @brief The result of having performed a scan.
* When a scan completes, we have a set of found devices. Each device is described
* by a BLEAdvertisedDevice object. The number of items in the set is given by
* @brief A class that contains and operates on the results of a BLE scan.
* @details When a scan completes, we have a set of found devices. Each device is described
* by a NimBLEAdvertisedDevice object. The number of items in the set is given by
* getCount(). We can retrieve a device by calling getDevice() passing in the
* index (starting at 0) of the desired device.
*/
@ -62,12 +62,13 @@ class NimBLEScan {
public:
bool start(uint32_t duration, void (*scanCompleteCB)(NimBLEScanResults), bool is_continue = false);
NimBLEScanResults start(uint32_t duration, bool is_continue = false);
bool isScanning();
void setAdvertisedDeviceCallbacks(NimBLEAdvertisedDeviceCallbacks* pAdvertisedDeviceCallbacks, bool wantDuplicates = false);
void setActiveScan(bool active);
void setInterval(uint16_t intervalMSecs);
void setWindow(uint16_t windowMSecs);
void setDuplicateFilter(bool active);
void setLimitedOnly(bool active);
void setDuplicateFilter(bool enabled);
void setLimitedOnly(bool enabled);
void setFilterPolicy(uint8_t filter);
bool stop();
void clearResults();
@ -76,8 +77,10 @@ public:
private:
NimBLEScan();
friend class NimBLEDevice;
NimBLEScan();
~NimBLEScan();
static int handleGapEvent(ble_gap_event* event, void* arg);
void onHostReset();

View File

@ -18,12 +18,6 @@
#include "NimBLESecurity.h"
#include "NimBLEDevice.h"
/**
* @brief This class is for backward compatibility with the bluedroid based library.
* Use the new security functions in NimBLEDevice instead.
* New callback functions in NimBLEServer and NimBLEClient.
*/
NimBLESecurity::NimBLESecurity() {
}
@ -33,6 +27,15 @@ NimBLESecurity::~NimBLESecurity() {
/**
* @brief Set requested authentication mode
* @param [in] auth_req A bitmask containing one or more of:
* * ESP_LE_AUTH_NO_BOND 0x00
* * ESP_LE_AUTH_BOND 0x01
* * ESP_LE_AUTH_REQ_MITM (1 << 2)
* * ESP_LE_AUTH_REQ_BOND_MITM (ESP_LE_AUTH_BOND | ESP_LE_AUTH_REQ_MITM)
* * ESP_LE_AUTH_REQ_SC_ONLY (1 << 3)
* * ESP_LE_AUTH_REQ_SC_BOND (ESP_LE_AUTH_BOND | ESP_LE_AUTH_REQ_SC_ONLY)
* * ESP_LE_AUTH_REQ_SC_MITM (ESP_LE_AUTH_REQ_MITM | ESP_LE_AUTH_REQ_SC_ONLY)
* * ESP_LE_AUTH_REQ_SC_MITM_BOND (ESP_LE_AUTH_REQ_MITM | ESP_LE_AUTH_REQ_SC_ONLY | ESP_LE_AUTH_BOND)
*/
void NimBLESecurity::setAuthenticationMode(esp_ble_auth_req_t auth_req) {
NimBLEDevice::setSecurityAuth((auth_req & BLE_SM_PAIR_AUTHREQ_BOND)>0,
@ -42,8 +45,15 @@ void NimBLESecurity::setAuthenticationMode(esp_ble_auth_req_t auth_req) {
/**
* @brief Set our device IO capability to let end user perform authorization
* either by displaying or entering generated 6-digits pin code
* @brief Set our device IO capability to let end user perform authorization
* either by displaying or entering generated 6-digit pin code or use \"just works\".
* @param [in] iocap The IO capabilites our device has.\n
* Can be set to one of:
* * ESP_IO_CAP_OUT 0
* * ESP_IO_CAP_IO 1
* * ESP_IO_CAP_IN 2
* * ESP_IO_CAP_NONE 3
* * ESP_IO_CAP_KBDISP 4
*/
void NimBLESecurity::setCapability(esp_ble_io_cap_t iocap) {
NimBLEDevice::setSecurityIOCap(iocap);
@ -51,8 +61,13 @@ void NimBLESecurity::setCapability(esp_ble_io_cap_t iocap) {
/**
* @brief Init encryption key by server
* @param key_size is value between 7 and 16
* @brief Sets the keys we will distibute during encryption.
* @param [in] init_key A bitmask of the keys we will distibute.\n
* Can be one or more of:
* * ESP_BLE_ENC_KEY_MASK (1 << 0)
* * ESP_BLE_ID_KEY_MASK (1 << 1)
* * ESP_BLE_CSR_KEY_MASK (1 << 2)
* * ESP_BLE_LINK_KEY_MASK (1 << 3)
*/
void NimBLESecurity::setInitEncryptionKey(uint8_t init_key) {
NimBLEDevice::setSecurityInitKey(init_key);
@ -60,8 +75,13 @@ void NimBLESecurity::setInitEncryptionKey(uint8_t init_key) {
/**
* @brief Init encryption key by client
* @param key_size is value between 7 and 16
* @brief Sets the keys we will accept during encryption.
* @param [in] resp_key A bitmask of the keys we will accept.\n
* Can be one or more of:
* * ESP_BLE_ENC_KEY_MASK (1 << 0)
* * ESP_BLE_ID_KEY_MASK (1 << 1)
* * ESP_BLE_CSR_KEY_MASK (1 << 2)
* * ESP_BLE_LINK_KEY_MASK (1 << 3)
*/
void NimBLESecurity::setRespEncryptionKey(uint8_t resp_key) {
NimBLEDevice::setSecurityRespKey(resp_key);
@ -70,7 +90,6 @@ void NimBLESecurity::setRespEncryptionKey(uint8_t resp_key) {
/**
*@todo Requires implementation
*
*/
void NimBLESecurity::setKeySize(uint8_t key_size) {
@ -80,7 +99,8 @@ void NimBLESecurity::setKeySize(uint8_t key_size) {
/**
* Setup for static PIN connection.
* @brief Sets a static PIN used to authenticate/encrypt the connection.
* @param [in] pin The 6 digit pin code to accept.
*/
void NimBLESecurity::setStaticPIN(uint32_t pin){
//uint32_t passkey = pin;

View File

@ -12,10 +12,6 @@
* Author: chegewara
*/
/** This class exists for backward compatibility - Should not be used in new code
* See the security functions in NimBLEDevice and callbacks in NimBLEServer / NimBLEClient
*/
#ifndef COMPONENTS_NIMBLESECURITY_H_
#define COMPONENTS_NIMBLESECURITY_H_
#include "sdkconfig.h"
@ -56,6 +52,12 @@
typedef uint8_t esp_ble_auth_req_t; /*!< combination of the above bit pattern */
typedef uint8_t esp_ble_io_cap_t; /*!< combination of the io capability */
/**
* @brief A class to handle BLE security operations.
* <b>Deprecated - provided for backward compatibility only.</b>
* @deprecated Use the security methods provided in NimBLEDevice instead.
*/
class NimBLESecurity {
public:
NimBLESecurity();
@ -78,8 +80,10 @@ private:
}; // BLESecurity
/*
* @brief Callbacks to handle GAP events related to authorization
/**
* @brief Callbacks to handle GAP events related to authorization.
* <b>Deprecated - provided for backward compatibility only.</b>
* @deprecated Use the callbacks provided in NimBLEClientCallbacks and NimBLEServerCallbacks instead.
*/
class NimBLESecurityCallbacks {
public:
@ -95,21 +99,25 @@ public:
/**
* @brief Provide us 6-digits code to perform authentication.
* It requires that our device is capable to display this code to end user
* @param
* @param [in] pass_key The PIN provided by the peer.
*/
virtual void onPassKeyNotify(uint32_t pass_key) = 0;
/**
* @brief Here we can make decision if we want to let negotiate authorization with peer device or not
* return Return true if we accept this peer device request
* @return Return true if we accept this peer device request
*/
virtual bool onSecurityRequest() = 0 ;
/**
* Provide us information when authentication process is completed
* @brief Provides us information when authentication process is completed
*/
virtual void onAuthenticationComplete(ble_gap_conn_desc*) = 0;
/**
* @brief Called when using numeric comparison for authentication.
* @param [in] pin The PIN to compare.
* @return True to accept and pair.
*/
virtual bool onConfirmPIN(uint32_t pin) = 0;
}; // BLESecurityCallbacks

View File

@ -22,6 +22,10 @@
#include "NimBLEDevice.h"
#include "NimBLELog.h"
#include "services/gap/ble_svc_gap.h"
#include "services/gatt/ble_svc_gatt.h"
static const char* LOG_TAG = "NimBLEServer";
static NimBLEServerCallbacks defaultCallbacks;
@ -37,9 +41,25 @@ NimBLEServer::NimBLEServer() {
m_pServerCallbacks = &defaultCallbacks;
m_gattsStarted = false;
m_advertiseOnDisconnect = true;
m_svcChanged = false;
m_deleteCallbacks = true;
} // NimBLEServer
/**
* @brief Destructor: frees all resources / attributes created.
*/
NimBLEServer::~NimBLEServer() {
for(auto &it : m_svcVec) {
delete it;
}
if(m_deleteCallbacks && m_pServerCallbacks != &defaultCallbacks) {
delete m_pServerCallbacks;
}
}
/**
* @brief Create a %BLE Service.
* @param [in] uuid The UUID of the new service.
@ -71,6 +91,12 @@ 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();
}
NIMBLE_LOGD(LOG_TAG, "<< createService");
return pService;
} // createService
@ -146,17 +172,21 @@ void NimBLEServer::start() {
NIMBLE_LOGI(LOG_TAG, "Service changed characterisic handle: %d", m_svcChgChrHdl);
*/
// Build a vector of characteristics with Notify / Indicate capabilities for event handling
// Get the assigned service handles and build a vector of characteristics
// with Notify / Indicate capabilities for event handling
for(auto &svc : m_svcVec) {
if(svc->m_removed == 0) {
rc = ble_gatts_find_svc(&svc->getUUID().getNative()->u, &svc->m_handle);
if(rc != 0) {
abort();
}
}
for(auto &chr : svc->m_chrVec) {
// if Notify / Indicate is enabled but we didn't create the descriptor
// we do it now.
if((chr->m_properties & BLE_GATT_CHR_F_INDICATE) ||
(chr->m_properties & BLE_GATT_CHR_F_NOTIFY)) {
if(nullptr == chr->getDescriptorByUUID(uint16_t(0x2902))) {
chr->createDescriptor(uint16_t(0x2902));
}
m_notifyChrVec.push_back(chr);
}
}
@ -168,8 +198,8 @@ void NimBLEServer::start() {
/**
* @brief Disconnect the specified client with optional reason.
* @param [in] Connection Id of the client to disconnect.
* @param [in] Reason code for disconnecting.
* @param [in] connId Connection Id of the client to disconnect.
* @param [in] reason code for disconnecting.
* @return NimBLE host return code.
*/
int NimBLEServer::disconnect(uint16_t connId, uint8_t reason) {
@ -188,7 +218,7 @@ int NimBLEServer::disconnect(uint16_t connId, uint8_t reason) {
/**
* @brief Set the server to automatically start advertising when a client disconnects.
* @param [in] bool true == advertise, false == don't advertise.
* @param [in] aod true == advertise, false == don't advertise.
*/
void NimBLEServer::advertiseOnDisconnect(bool aod) {
m_advertiseOnDisconnect = aod;
@ -260,6 +290,11 @@ size_t NimBLEServer::getConnectedCount() {
server->m_connectedPeersVec.end(),
event->disconnect.conn.conn_handle),
server->m_connectedPeersVec.end());
if(server->m_svcChanged) {
server->resetGATT();
}
server->m_pServerCallbacks->onDisconnect(server);
if(server->m_advertiseOnDisconnect) {
@ -269,9 +304,9 @@ size_t NimBLEServer::getConnectedCount() {
} // BLE_GAP_EVENT_DISCONNECT
case BLE_GAP_EVENT_SUBSCRIBE: {
NIMBLE_LOGI(LOG_TAG, "subscribe event; cur_notify=%d\n value handle; "
"val_handle=%d\n",
event->subscribe.cur_notify, event->subscribe.attr_handle);
NIMBLE_LOGI(LOG_TAG, "subscribe event; attr_handle=%d, subscribed: %s",
event->subscribe.attr_handle,
(event->subscribe.cur_notify ? "true":"false"));
for(auto &it : server->m_notifyChrVec) {
if(it->getHandle() == event->subscribe.attr_handle) {
@ -318,6 +353,12 @@ size_t NimBLEServer::getConnectedCount() {
return 0;
} // BLE_GAP_EVENT_NOTIFY_TX
case BLE_GAP_EVENT_ADV_COMPLETE: {
NIMBLE_LOGD(LOG_TAG, "Advertising Complete");
NimBLEDevice::getAdvertising()->advCompleteCB();
return 0;
}
case BLE_GAP_EVENT_CONN_UPDATE: {
NIMBLE_LOGD(LOG_TAG, "Connection parameters updated.");
return 0;
@ -435,16 +476,121 @@ size_t NimBLEServer::getConnectedCount() {
* events are detected.
*
* @param [in] pCallbacks The callbacks to be invoked.
* @param [in] deleteCallbacks if true callback class will be deleted when server is destructed.
*/
void NimBLEServer::setCallbacks(NimBLEServerCallbacks* pCallbacks) {
void NimBLEServer::setCallbacks(NimBLEServerCallbacks* pCallbacks, bool deleteCallbacks) {
if (pCallbacks != nullptr){
m_pServerCallbacks = pCallbacks;
m_deleteCallbacks = deleteCallbacks;
} else {
m_pServerCallbacks = &defaultCallbacks;
}
} // setCallbacks
/**
* @brief Remove a service from the server.
*
* @details Immediately removes access to the service by clients, sends a service changed indication,
* and removes the service (if applicable) from the advertisments.
* The service is not deleted unless the deleteSvc parameter is true, otherwise the service remains
* available and can be re-added in the future. If desired a removed but not deleted service can
* be deleted later by calling this method with deleteSvc set to true.
*
* @note The service will not be removed from the database until all open connections are closed
* as it requires resetting the GATT server. In the interim the service will have it's visibility disabled.
*
* @note Advertising will need to be restarted by the user after calling this as we must stop
* advertising in order to remove the service.
*
* @param [in] service The service object to remove.
* @param [in] deleteSvc true if the service should be deleted.
*/
void NimBLEServer::removeService(NimBLEService* service, bool deleteSvc) {
// Check if the service 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(service->m_removed > 0) {
if(deleteSvc) {
for(auto it = m_svcVec.begin(); it != m_svcVec.end(); ++it) {
if ((*it)->getUUID() == service->getUUID()) {
delete *it;
m_svcVec.erase(it);
break;
}
}
}
return;
}
int rc = ble_gatts_svc_set_visibility(service->getHandle(), 0);
if(rc !=0) {
return;
}
service->m_removed = deleteSvc ? 2 : 1;
m_svcChanged = true;
ble_svc_gatt_changed(0x0001, 0xffff);
resetGATT();
NimBLEDevice::getAdvertising()->removeServiceUUID(service->getUUID());
}
/**
* @brief Adds a service which was already created, but removed from availability.
* @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.
if(service->m_removed == 0) {
return;
}
service->m_removed = 0;
m_svcChanged = true;
ble_svc_gatt_changed(0x0001, 0xffff);
resetGATT();
}
/**
* @brief Resets the GATT server, used when services are added/removed after initialization.
*/
void NimBLEServer::resetGATT() {
if(getConnectedCount() > 0) {
return;
}
NimBLEDevice::stopAdvertising();
ble_gatts_reset();
ble_svc_gap_init();
ble_svc_gatt_init();
for(auto it = m_svcVec.begin(); it != m_svcVec.end(); ) {
if ((*it)->m_removed > 0) {
if ((*it)->m_removed == 2) {
delete *it;
it = m_svcVec.erase(it);
} else {
++it;
}
continue;
}
(*it)->start();
++it;
}
m_svcChanged = false;
m_gattsStarted = false;
}
/**
* @brief Start advertising.
*
@ -516,7 +662,7 @@ uint32_t NimBLEServerCallbacks::onPassKeyRequest(){
NIMBLE_LOGD("NimBLEServerCallbacks", "onPassKeyRequest: default: 123456");
return 123456;
}
/*
void NimBLEServerCallbacks::onPassKeyNotify(uint32_t pass_key){
NIMBLE_LOGD("NimBLEServerCallbacks", "onPassKeyNotify: default: %d", pass_key);
}
@ -525,6 +671,7 @@ bool NimBLEServerCallbacks::onSecurityRequest(){
NIMBLE_LOGD("NimBLEServerCallbacks", "onSecurityRequest: default: true");
return true;
}
*/
void NimBLEServerCallbacks::onAuthenticationComplete(ble_gap_conn_desc*){
NIMBLE_LOGD("NimBLEServerCallbacks", "onAuthenticationComplete: default");
}

View File

@ -41,8 +41,11 @@ public:
NimBLEService* createService(const char* uuid);
NimBLEService* createService(const NimBLEUUID &uuid, uint32_t numHandles=15,
uint8_t inst_id=0);
void removeService(NimBLEService* service, bool deleteSvc = false);
void addService(NimBLEService* service);
NimBLEAdvertising* getAdvertising();
void setCallbacks(NimBLEServerCallbacks* pCallbacks);
void setCallbacks(NimBLEServerCallbacks* pCallbacks,
bool deleteCallbacks = true);
void startAdvertising();
void stopAdvertising();
void start();
@ -54,18 +57,21 @@ 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();
void advertiseOnDisconnect(bool);
private:
NimBLEServer();
~NimBLEServer();
friend class NimBLECharacteristic;
friend class NimBLEDevice;
friend class NimBLEAdvertising;
bool m_gattsStarted;
bool m_advertiseOnDisconnect;
bool m_svcChanged;
NimBLEServerCallbacks* m_pServerCallbacks;
bool m_deleteCallbacks;
std::vector<uint16_t> m_connectedPeersVec;
// uint16_t m_svcChgChrHdl; // Future use
@ -74,6 +80,7 @@ private:
std::vector<NimBLECharacteristic*> m_notifyChrVec;
static int handleGapEvent(struct ble_gap_event *event, void *arg);
void resetGATT();
}; // NimBLEServer
@ -83,29 +90,52 @@ private:
class NimBLEServerCallbacks {
public:
virtual ~NimBLEServerCallbacks() {};
/**
* @brief Handle a new client connection.
*
* When a new client connects, we are invoked.
*
* @param [in] pServer A reference to the %BLE server that received the client connection.
* @brief Handle a client connection.
* This is called when a client connects.
* @param [in] pServer A pointer to the %BLE server that received the client connection.
*/
virtual void onConnect(NimBLEServer* pServer);
virtual void onConnect(NimBLEServer* pServer, ble_gap_conn_desc* desc);
/**
* @brief Handle an existing client disconnection.
*
* When an existing client disconnects, we are invoked.
*
* @brief Handle a client connection.
* This is called when a client connects.
* @param [in] pServer A pointer to the %BLE server that received the client connection.
* @param [in] desc A pointer to the connection description structure containig information
* about the connection parameters.
*/
virtual void onConnect(NimBLEServer* pServer, ble_gap_conn_desc* desc);
/**
* @brief Handle a client disconnection.
* This is called when a client disconnects.
* @param [in] pServer A reference to the %BLE server that received the existing client disconnection.
*/
virtual void onDisconnect(NimBLEServer* pServer);
virtual uint32_t onPassKeyRequest(); //{return 0;}
virtual void onPassKeyNotify(uint32_t pass_key); //{}
virtual bool onSecurityRequest(); //{return true;}
virtual void onAuthenticationComplete(ble_gap_conn_desc* desc);//{};
virtual bool onConfirmPIN(uint32_t pin);//{return true;}
/**
* @brief Called when a client requests a passkey for pairing.
* @return The passkey to be sent to the client.
*/
virtual uint32_t onPassKeyRequest();
//virtual void onPassKeyNotify(uint32_t pass_key);
//virtual bool onSecurityRequest();
/**
* @brief Called when the pairing procedure is complete.
* @param [in] desc A pointer to the struct containing the connection information.\n
* This can be used to check the status of the connection encryption/pairing.
*/
virtual void onAuthenticationComplete(ble_gap_conn_desc* desc);
/**
* @brief Called when using numeric comparision for pairing.
* @param [in] pin The pin to compare with the client.
* @return True to accept the pin.
*/
virtual bool onConfirmPIN(uint32_t pin);
}; // NimBLEServerCallbacks

View File

@ -49,13 +49,35 @@ NimBLEService::NimBLEService(const char* uuid, uint16_t numHandles, NimBLEServer
* @param [in] a pointer to the server instance that this service belongs to.
*/
NimBLEService::NimBLEService(const NimBLEUUID &uuid, uint16_t numHandles, NimBLEServer* pServer) {
m_uuid = uuid;
m_handle = NULL_HANDLE;
m_pServer = pServer;
m_numHandles = numHandles;
m_uuid = uuid;
m_handle = NULL_HANDLE;
m_pServer = pServer;
m_numHandles = numHandles;
m_pSvcDef = nullptr;
m_removed = 0;
} // NimBLEService
NimBLEService::~NimBLEService() {
if(m_pSvcDef != nullptr) {
if(m_pSvcDef->characteristics != nullptr) {
for(int i=0; m_pSvcDef->characteristics[i].uuid != NULL; ++i) {
if(m_pSvcDef->characteristics[i].descriptors) {
delete(m_pSvcDef->characteristics[i].descriptors);
}
}
delete(m_pSvcDef->characteristics);
}
delete(m_pSvcDef);
}
for(auto &it : m_chrVec) {
delete it;
}
}
/**
* @brief Dump details of this BLE GATT service.
* @return N/A.
@ -94,98 +116,84 @@ NimBLEUUID NimBLEService::getUUID() {
* and registers it with the NimBLE stack.
* @return bool success/failure .
*/
bool NimBLEService::start() {
NIMBLE_LOGD(LOG_TAG, ">> start(): Starting service: %s", toString().c_str());
int rc = 0;
// Nimble requires an array of services to be sent to the api
// Since we are adding 1 at a time we create an array of 2 and set the type
// of the second service to 0 to indicate the end of the array.
ble_gatt_svc_def* svc = new ble_gatt_svc_def[2];
ble_gatt_chr_def* pChr_a = nullptr;
ble_gatt_dsc_def* pDsc_a = nullptr;
svc[0].type = BLE_GATT_SVC_TYPE_PRIMARY;
svc[0].uuid = &m_uuid.getNative()->u;
svc[0].includes = NULL;
if(m_pSvcDef == nullptr) {
// Nimble requires an array of services to be sent to the api
// Since we are adding 1 at a time we create an array of 2 and set the type
// of the second service to 0 to indicate the end of the array.
ble_gatt_svc_def* svc = new ble_gatt_svc_def[2];
ble_gatt_chr_def* pChr_a = nullptr;
ble_gatt_dsc_def* pDsc_a = nullptr;
size_t numChrs = m_chrVec.size();
svc[0].type = BLE_GATT_SVC_TYPE_PRIMARY;
svc[0].uuid = &m_uuid.getNative()->u;
svc[0].includes = NULL;
NIMBLE_LOGD(LOG_TAG,"Adding %d characteristics for service %s", numChrs, toString().c_str());
size_t numChrs = m_chrVec.size();
if(!numChrs){
svc[0].characteristics = NULL;
}else{
// 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();
NIMBLE_LOGD(LOG_TAG,"Adding %d characteristics for service %s", numChrs, toString().c_str());
for(uint8_t i=0; i < numChrs;) {
uint8_t numDscs = pCharacteristic->m_dscVec.size();
if(numDscs) {
// skip 2902 as it's automatically created by NimBLE
// if Indicate or Notify flags are set
if(((pCharacteristic->m_properties & BLE_GATT_CHR_F_INDICATE) ||
(pCharacteristic->m_properties & BLE_GATT_CHR_F_NOTIFY)) &&
pCharacteristic->getDescriptorByUUID("2902") != nullptr)
{
numDscs--;
}
}
if(!numChrs){
svc[0].characteristics = NULL;
}else{
// 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();
if(!numDscs){
pChr_a[i].descriptors = NULL;
} else {
// Must have last descriptor uuid = 0 so we have to create 1 extra
//NIMBLE_LOGD(LOG_TAG, "Adding %d descriptors", numDscs);
pDsc_a = new ble_gatt_dsc_def[numDscs+1];
NimBLEDescriptor* pDescriptor = *pCharacteristic->m_dscVec.begin();
for(uint8_t d=0; d < numDscs;) {
// skip 2902
if(pDescriptor->m_uuid == NimBLEUUID(uint16_t(0x2902))) {
//NIMBLE_LOGD(LOG_TAG, "Skipped 0x2902");
pDescriptor = *(pCharacteristic->m_dscVec.begin()+d+1);
continue;
for(uint8_t i=0; i < numChrs;) {
uint8_t numDscs = pCharacteristic->m_dscVec.size();
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;
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].uuid = &pDescriptor->m_uuid.getNative()->u;
pDsc_a[d].att_flags = pDescriptor->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[numDscs].uuid = NULL;
pChr_a[i].descriptors = pDsc_a;
}
pDsc_a[numDscs].uuid = NULL;
pChr_a[i].descriptors = pDsc_a;
pChr_a[i].uuid = &pCharacteristic->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].min_key_size = 0;
pChr_a[i].val_handle = &pCharacteristic->m_handle;
i++;
pCharacteristic = *(m_chrVec.begin() + i);
}
pChr_a[i].uuid = &pCharacteristic->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].min_key_size = 0;
pChr_a[i].val_handle = &pCharacteristic->m_handle;
i++;
pCharacteristic = *(m_chrVec.begin() + i);
pChr_a[numChrs].uuid = NULL;
svc[0].characteristics = pChr_a;
}
pChr_a[numChrs].uuid = NULL;
svc[0].characteristics = pChr_a;
// end of services must indicate to api with type = 0
svc[1].type = 0;
m_pSvcDef = svc;
}
// end of services must indicate to api with type = 0
svc[1].type = 0;
rc = ble_gatts_count_cfg((const ble_gatt_svc_def*)svc);
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;
}
rc = ble_gatts_add_svcs((const ble_gatt_svc_def*)svc);
rc = ble_gatts_add_svcs((const ble_gatt_svc_def*)m_pSvcDef);
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "ble_gatts_add_svcs, rc= %d, %s", rc, NimBLEUtils::returnCodeToString(rc));
return false;
@ -238,11 +246,21 @@ NimBLECharacteristic* NimBLEService::createCharacteristic(const NimBLEUUID &uuid
} // createCharacteristic
/**
* @brief Get a pointer to the characteristic object with the specified UUID.
* @param [in] uuid The UUID of the characteristic.
* @return A pointer to the characteristic object or nullptr if not found.
*/
NimBLECharacteristic* NimBLEService::getCharacteristic(const char* uuid) {
return getCharacteristic(NimBLEUUID(uuid));
}
/**
* @brief Get a pointer to the characteristic object with the specified UUID.
* @param [in] uuid The UUID of the characteristic.
* @return A pointer to the characteristic object or nullptr if not found.
*/
NimBLECharacteristic* NimBLEService::getCharacteristic(const NimBLEUUID &uuid) {
for (auto &it : m_chrVec) {
if (it->getUUID() == uuid) {

View File

@ -57,6 +57,7 @@ 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;
@ -65,7 +66,8 @@ private:
NimBLEServer* m_pServer;
NimBLEUUID m_uuid;
uint16_t m_numHandles;
ble_gatt_svc_def* m_pSvcDef;
uint8_t m_removed;
std::vector<NimBLECharacteristic*> m_chrVec;
}; // NimBLEService

View File

@ -73,15 +73,11 @@ static const char* LOG_TAG = "NimBLEUUID";
/**
* @brief Create a UUID from 16 bytes of memory.
*
* @param [in] pData The pointer to the start of the UUID.
* @param [in] size The size of the data.
* @param [in] msbFirst Is the MSB first in pData memory?
*/
NimBLEUUID::NimBLEUUID(const uint8_t* pData, size_t size, bool msbFirst) {
/*** TODO: change this to use the Nimble function for various lenght UUIDs:
int ble_uuid_init_from_buf(ble_uuid_any_t *uuid, const void *buf, size_t len);
***/
if (size != 16) {
NIMBLE_LOGE(LOG_TAG,"ERROR: UUID length not 16 bytes");
return;
@ -99,7 +95,6 @@ NimBLEUUID::NimBLEUUID(const uint8_t* pData, size_t size, bool msbFirst) {
/**
* @brief Create a UUID from the 16bit value.
*
* @param [in] uuid The 16bit short form UUID.
*/
NimBLEUUID::NimBLEUUID(uint16_t uuid) {
@ -111,7 +106,6 @@ NimBLEUUID::NimBLEUUID(uint16_t uuid) {
/**
* @brief Create a UUID from the 32bit value.
*
* @param [in] uuid The 32bit short form UUID.
*/
NimBLEUUID::NimBLEUUID(uint32_t uuid) {
@ -123,7 +117,6 @@ NimBLEUUID::NimBLEUUID(uint32_t uuid) {
/**
* @brief Create a UUID from the native UUID.
*
* @param [in] uuid The native UUID.
*/
NimBLEUUID::NimBLEUUID(const ble_uuid128_t* uuid) {
@ -135,8 +128,8 @@ NimBLEUUID::NimBLEUUID(const ble_uuid128_t* uuid) {
/**
* @brief Create a UUID from the 128bit value using hex parts instead of string,
* instead of BLEUUID("ebe0ccb0-7a0a-4b0c-8a1a-6ff2997da3a6"), it becomes
* BLEUUID(0xebe0ccb0, 0x7a0a, 0x4b0c, 0x8a1a6ff2997da3a6)
* instead of NimBLEUUID("ebe0ccb0-7a0a-4b0c-8a1a-6ff2997da3a6"), it becomes
* NimBLEUUID(0xebe0ccb0, 0x7a0a, 0x4b0c, 0x8a1a6ff2997da3a6)
*
* @param [in] first The first 32bit of the UUID.
* @param [in] second The next 16bit of the UUID.
@ -153,6 +146,9 @@ NimBLEUUID::NimBLEUUID(uint32_t first, uint16_t second, uint16_t third, uint64_t
}
/**
* @brief Creates an empty UUID.
*/
NimBLEUUID::NimBLEUUID() {
m_valueSet = false;
} // NimBLEUUID
@ -180,29 +176,31 @@ bool NimBLEUUID::equals(const NimBLEUUID &uuid) const {
/**
* Create a BLEUUID from a string of the form:
* Create a NimBLEUUID from a string of the form:
* 0xNNNN
* 0xNNNNNNNN
* 0x<UUID>
* 0x<UUID\>
* NNNN
* NNNNNNNN
* <UUID>
* <UUID\>
*
* @param [in] uuid The string to create the UUID from.
*/
NimBLEUUID NimBLEUUID::fromString(const std::string &_uuid) {
NimBLEUUID NimBLEUUID::fromString(const std::string &uuid) {
uint8_t start = 0;
if (strstr(_uuid.c_str(), "0x") != nullptr) { // If the string starts with 0x, skip those characters.
if (strstr(uuid.c_str(), "0x") != nullptr) { // If the string starts with 0x, skip those characters.
start = 2;
}
uint8_t len = _uuid.length() - start; // Calculate the length of the string we are going to use.
uint8_t len = uuid.length() - start; // Calculate the length of the string we are going to use.
if(len == 4) {
uint16_t x = strtoul(_uuid.substr(start, len).c_str(), NULL, 16);
uint16_t x = strtoul(uuid.substr(start, len).c_str(), NULL, 16);
return NimBLEUUID(x);
} else if (len == 8) {
uint32_t x = strtoul(_uuid.substr(start, len).c_str(), NULL, 16);
uint32_t x = strtoul(uuid.substr(start, len).c_str(), NULL, 16);
return NimBLEUUID(x);
} else if (len == 36) {
return NimBLEUUID(_uuid);
return NimBLEUUID(uuid);
}
return NimBLEUUID();
} // fromString
@ -210,8 +208,7 @@ NimBLEUUID NimBLEUUID::fromString(const std::string &_uuid) {
/**
* @brief Get the native UUID value.
*
* @return The native UUID value or NULL if not set.
* @return The native UUID value or nullptr if not set.
*/
const ble_uuid_any_t* NimBLEUUID::getNative() const {
if (m_valueSet == false) {
@ -224,9 +221,9 @@ const ble_uuid_any_t* NimBLEUUID::getNative() const {
/**
* @brief Convert a UUID to its 128 bit representation.
*
* A UUID can be internally represented as 16bit, 32bit or the full 128bit. This method
* @details A UUID can be internally represented as 16bit, 32bit or the full 128bit. This method
* will convert 16 or 32 bit representations to the full 128bit.
* @return The NimBLEUUID converted to 128bit.
*/
const NimBLEUUID &NimBLEUUID::to128() {
// If we either don't have a value or are already a 128 bit UUID, nothing further to do.
@ -248,19 +245,23 @@ const NimBLEUUID &NimBLEUUID::to128() {
/**
* @brief Get a string representation of the UUID.
*
* @details
* The format of a string is:
* 01234567 8901 2345 6789 012345678901
* 0000180d-0000-1000-8000-00805f9b34fb
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
*
* @return A string representation of the UUID.
* @deprecated Use std::string() operator instead.
*/
std::string NimBLEUUID::toString() const {
return std::string(*this);
} // toString
/**
* @brief Convienience operator to check if this UUID is equal to another.
*/
bool NimBLEUUID::operator ==(const NimBLEUUID & rhs) const {
if(m_valueSet && rhs.m_valueSet) {
return ble_uuid_cmp(&m_uuid.u, &rhs.m_uuid.u) == 0;
@ -270,11 +271,19 @@ bool NimBLEUUID::operator ==(const NimBLEUUID & rhs) const {
}
/**
* @brief Convienience operator to check if this UUID is not equal to another.
*/
bool NimBLEUUID::operator !=(const NimBLEUUID & rhs) const {
return !this->operator==(rhs);
}
/**
* @brief Convienience operator to convert this UUID to string representation.
* @details This allows passing NimBLEUUID to functions
* that accept std::string and/or or it's methods as a parameter.
*/
NimBLEUUID::operator std::string() const {
if (!m_valueSet) return std::string(); // If we have no value, nothing to format.

View File

@ -3,7 +3,7 @@
*
* Created: on Jan 24 2020
* Author H2zero
*
*
* Originally:
*
* BLEUUID.h
@ -37,20 +37,21 @@ public:
NimBLEUUID(const uint8_t* pData, size_t size, bool msbFirst);
NimBLEUUID(uint32_t first, uint16_t second, uint16_t third, uint64_t fourth);
NimBLEUUID();
uint8_t bitSize() const; // Get the number of bits in this uuid.
bool equals(const NimBLEUUID &uuid) const;
uint8_t bitSize() const;
bool equals(const NimBLEUUID &uuid) const;
const ble_uuid_any_t* getNative() const;
const NimBLEUUID & to128();
std::string toString() const;
static NimBLEUUID fromString(const std::string &uuid); // Create a NimBLEUUID from a string
const NimBLEUUID & to128();
std::string toString() const;
static NimBLEUUID fromString(const std::string &uuid);
bool operator ==(const NimBLEUUID & rhs) const;
bool operator !=(const NimBLEUUID & rhs) const;
operator std::string() const;
private:
ble_uuid_any_t m_uuid; // The underlying UUID structure that this class wraps.
bool m_valueSet = false; // Is there a value set for this instance.
ble_uuid_any_t m_uuid;
bool m_valueSet = false;
}; // NimBLEUUID
#endif /* CONFIG_BT_ENABLED */
#endif /* COMPONENTS_NIMBLEUUID_H_ */

View File

@ -16,6 +16,11 @@
static const char* LOG_TAG = "NimBLEUtils";
/**
* @brief A function for checking validity of connection parameters.
* @param [in] params A pointer to the structure containing the parameters to check.
* @return valid == 0 or error code.
*/
int NimBLEUtils::checkConnParams(ble_gap_conn_params* params) {
/* Check connection interval min */
if ((params->itvl_min < BLE_HCI_CONN_ITVL_MIN) ||
@ -49,6 +54,11 @@ int NimBLEUtils::checkConnParams(ble_gap_conn_params* params) {
}
/**
* @brief Converts a return code from the NimBLE stack to a text string.
* @param [in] rc The return code to convert.
* @return A string representation of the return code.
*/
const char* NimBLEUtils::returnCodeToString(int rc) {
#if defined(CONFIG_NIMBLE_CPP_ENABLE_RETURN_CODE_TEXT)
switch(rc) {
@ -338,9 +348,9 @@ const char* NimBLEUtils::returnCodeToString(int rc) {
/**
* @brief Convert the BLE Advertising Data flags to a string.
* @param adFlags The flags to convert
* @return std::string A string representation of the advertising flags.
* @brief Convert the advertising type flag to a string.
* @param advType The type to convert.
* @return A string representation of the advertising flags.
*/
const char* NimBLEUtils::advTypeToString(uint8_t advType) {
#if defined(CONFIG_NIMBLE_CPP_ENABLE_ADVERTISMENT_TYPE_TEXT)
@ -367,7 +377,7 @@ const char* NimBLEUtils::advTypeToString(uint8_t advType) {
/**
* @brief Create a hex representation of data.
*
* @param [in] target Where to write the hex string. If this is null, we malloc storage.
* @param [in] target Where to write the hex string. If this is null, we malloc storage.
* @param [in] source The start of the binary data.
* @param [in] length The length of the data to convert.
* @return A pointer to the formatted buffer.
@ -400,6 +410,11 @@ char* NimBLEUtils::buildHexData(uint8_t* target, const uint8_t* source, uint8_t
} // buildHexData
/**
* @brief Utility function to log the gap event info.
* @param [in] event A pointer to the gap event structure.
* @param [in] arg Unused.
*/
void NimBLEUtils::dumpGapEvent(ble_gap_event *event, void *arg){
#if defined(CONFIG_NIMBLE_CPP_ENABLE_GAP_EVENT_CODE_TEXT)
NIMBLE_LOGD(LOG_TAG, "Received a GAP event: %s", gapEventToString(event->type));
@ -408,7 +423,7 @@ void NimBLEUtils::dumpGapEvent(ble_gap_event *event, void *arg){
/**
* @brief Convert a BT GAP event type to a string representation.
* @brief Convert a GAP event type to a string representation.
* @param [in] eventType The type of event.
* @return A string representation of the event type.
*/
@ -493,198 +508,4 @@ const char* NimBLEUtils::gapEventToString(uint8_t eventType) {
#endif // #if defined(CONFIG_NIMBLE_CPP_ENABLE_GAP_EVENT_CODE_TEXT)
} // gapEventToString
/**
* Utility function to log an array of bytes.
*/
void print_bytes(const uint8_t *bytes, int len)
{
int i;
for (i = 0; i < len; i++) {
MODLOG_DFLT(ERROR, "%s0x%02x", i != 0 ? ":" : "", bytes[i]);
if(i % 30 == 0){
MODLOG_DFLT(ERROR, "\n");
}
}
MODLOG_DFLT(ERROR, "\n");
}
void print_mbuf(const struct os_mbuf *om)
{
int colon;
colon = 0;
while (om != NULL) {
if (colon) {
MODLOG_DFLT(DEBUG, ":");
} else {
colon = 1;
}
print_bytes(om->om_data, om->om_len);
om = SLIST_NEXT(om, om_next);
}
}
char *addr_str(const void *addr)
{
static char buf[6 * 2 + 5 + 1];
const uint8_t *u8p;
u8p = (const uint8_t*)addr;
sprintf(buf, "%02x:%02x:%02x:%02x:%02x:%02x",
u8p[5], u8p[4], u8p[3], u8p[2], u8p[1], u8p[0]);
return buf;
}
void print_uuid(const ble_uuid_t *uuid)
{
char buf[BLE_UUID_STR_LEN];
MODLOG_DFLT(DEBUG, "%s", ble_uuid_to_str(uuid, buf));
}
void print_adv_fields(const struct ble_hs_adv_fields *fields)
{
char s[BLE_HS_ADV_MAX_SZ];
const uint8_t *u8p;
int i;
if (fields->flags != 0) {
MODLOG_DFLT(DEBUG, " flags=0x%02x\n", fields->flags);
}
if (fields->uuids16 != NULL) {
MODLOG_DFLT(DEBUG, " uuids16(%scomplete)=",
fields->uuids16_is_complete ? "" : "in");
for (i = 0; i < fields->num_uuids16; i++) {
print_uuid(&fields->uuids16[i].u);
MODLOG_DFLT(DEBUG, " ");
}
MODLOG_DFLT(DEBUG, "\n");
}
if (fields->uuids32 != NULL) {
MODLOG_DFLT(DEBUG, " uuids32(%scomplete)=",
fields->uuids32_is_complete ? "" : "in");
for (i = 0; i < fields->num_uuids32; i++) {
print_uuid(&fields->uuids32[i].u);
MODLOG_DFLT(DEBUG, " ");
}
MODLOG_DFLT(DEBUG, "\n");
}
if (fields->uuids128 != NULL) {
MODLOG_DFLT(DEBUG, " uuids128(%scomplete)=",
fields->uuids128_is_complete ? "" : "in");
for (i = 0; i < fields->num_uuids128; i++) {
print_uuid(&fields->uuids128[i].u);
MODLOG_DFLT(DEBUG, " ");
}
MODLOG_DFLT(DEBUG, "\n");
}
if (fields->name != NULL) {
assert(fields->name_len < sizeof s - 1);
memcpy(s, fields->name, fields->name_len);
s[fields->name_len] = '\0';
MODLOG_DFLT(DEBUG, " name(%scomplete)=%s\n",
fields->name_is_complete ? "" : "in", s);
}
if (fields->tx_pwr_lvl_is_present) {
MODLOG_DFLT(DEBUG, " tx_pwr_lvl=%d\n", fields->tx_pwr_lvl);
}
if (fields->slave_itvl_range != NULL) {
MODLOG_DFLT(DEBUG, " slave_itvl_range=");
print_bytes(fields->slave_itvl_range, BLE_HS_ADV_SLAVE_ITVL_RANGE_LEN);
MODLOG_DFLT(DEBUG, "\n");
}
if (fields->svc_data_uuid16 != NULL) {
MODLOG_DFLT(DEBUG, " svc_data_uuid16=");
print_bytes(fields->svc_data_uuid16, fields->svc_data_uuid16_len);
MODLOG_DFLT(DEBUG, "\n");
}
if (fields->public_tgt_addr != NULL) {
MODLOG_DFLT(DEBUG, " public_tgt_addr=");
u8p = fields->public_tgt_addr;
for (i = 0; i < fields->num_public_tgt_addrs; i++) {
MODLOG_DFLT(DEBUG, "public_tgt_addr=%s ", addr_str(u8p));
u8p += BLE_HS_ADV_PUBLIC_TGT_ADDR_ENTRY_LEN;
}
MODLOG_DFLT(DEBUG, "\n");
}
if (fields->appearance_is_present) {
MODLOG_DFLT(DEBUG, " appearance=0x%04x\n", fields->appearance);
}
if (fields->adv_itvl_is_present) {
MODLOG_DFLT(DEBUG, " adv_itvl=0x%04x\n", fields->adv_itvl);
}
if (fields->svc_data_uuid32 != NULL) {
MODLOG_DFLT(DEBUG, " svc_data_uuid32=");
print_bytes(fields->svc_data_uuid32, fields->svc_data_uuid32_len);
MODLOG_DFLT(DEBUG, "\n");
}
if (fields->svc_data_uuid128 != NULL) {
MODLOG_DFLT(DEBUG, " svc_data_uuid128=");
print_bytes(fields->svc_data_uuid128, fields->svc_data_uuid128_len);
MODLOG_DFLT(DEBUG, "\n");
}
if (fields->uri != NULL) {
MODLOG_DFLT(DEBUG, " uri=");
print_bytes(fields->uri, fields->uri_len);
MODLOG_DFLT(DEBUG, "\n");
}
if (fields->mfg_data != NULL) {
MODLOG_DFLT(DEBUG, " mfg_data=");
print_bytes(fields->mfg_data, fields->mfg_data_len);
MODLOG_DFLT(DEBUG, "\n");
}
}
/**
* Logs information about a connection to the console.
*/
void print_conn_desc(const struct ble_gap_conn_desc *desc)
{
MODLOG_DFLT(DEBUG, "handle=%d our_ota_addr_type=%d our_ota_addr=%s ",
desc->conn_handle, desc->our_ota_addr.type,
addr_str(desc->our_ota_addr.val));
MODLOG_DFLT(DEBUG, "our_id_addr_type=%d our_id_addr=%s ",
desc->our_id_addr.type, addr_str(desc->our_id_addr.val));
MODLOG_DFLT(DEBUG, "peer_ota_addr_type=%d peer_ota_addr=%s ",
desc->peer_ota_addr.type, addr_str(desc->peer_ota_addr.val));
MODLOG_DFLT(DEBUG, "peer_id_addr_type=%d peer_id_addr=%s ",
desc->peer_id_addr.type, addr_str(desc->peer_id_addr.val));
MODLOG_DFLT(DEBUG, "conn_itvl=%d conn_latency=%d supervision_timeout=%d "
"encrypted=%d authenticated=%d bonded=%d",
desc->conn_itvl, desc->conn_latency,
desc->supervision_timeout,
desc->sec_state.encrypted,
desc->sec_state.authenticated,
desc->sec_state.bonded);
}
void print_addr(const void *addr)
{
const uint8_t *u8p;
u8p = (uint8_t*)addr;
MODLOG_DFLT(INFO, "%02x:%02x:%02x:%02x:%02x:%02x",
u8p[5], u8p[4], u8p[3], u8p[2], u8p[1], u8p[0]);
}
#endif //CONFIG_BT_ENABLED

View File

@ -27,14 +27,10 @@ typedef struct {
std::string *buf;
} ble_task_data_t;
extern "C"{
char *addr_str(const void *addr);
void print_conn_desc(const struct ble_gap_conn_desc *desc);
void print_adv_fields(const struct ble_hs_adv_fields *fields);
void print_addr(const void *addr);
void print_bytes(const uint8_t *bytes, int len);
}
/**
* @brief A BLE Utility class with methods for debugging and general purpose use.
*/
class NimBLEUtils {
public:
static void dumpGapEvent(ble_gap_event *event, void *arg);

View File

@ -30,6 +30,7 @@
#include "esp_bt.h"
#include "freertos/semphr.h"
#include "esp_compiler.h"
#include "esp_ipc.h"
#define NIMBLE_VHCI_TIMEOUT_MS 2000
@ -78,21 +79,29 @@ void ble_hci_trans_cfg_hs(ble_hci_trans_rx_cmd_fn *cmd_cb,
ble_hci_rx_acl_hs_arg = acl_arg;
}
void ble_hci_trans_hs_cmd_tx_on_core_0(void *arg)
{
uint8_t *cmd = arg;
uint16_t len = BLE_HCI_CMD_HDR_LEN + cmd[3] + 1;
esp_vhci_host_send_packet(cmd, len);
}
int ble_hci_trans_hs_cmd_tx(uint8_t *cmd)
{
uint16_t len;
uint8_t rc = 0;
assert(cmd != NULL);
*cmd = BLE_HCI_UART_H4_CMD;
len = BLE_HCI_CMD_HDR_LEN + cmd[3] + 1;
if (!esp_vhci_host_check_send_available()) {
ESP_LOGD(TAG, "Controller not ready to receive packets");
}
if (xSemaphoreTake(vhci_send_sem, NIMBLE_VHCI_TIMEOUT_MS / portTICK_PERIOD_MS) == pdTRUE) {
esp_vhci_host_send_packet(cmd, len);
if (xPortGetCoreID() != 0) {
esp_ipc_call_blocking(0, ble_hci_trans_hs_cmd_tx_on_core_0, cmd);
} else {
ble_hci_trans_hs_cmd_tx_on_core_0(cmd);
}
} else {
rc = BLE_HS_ETIMEOUT_HCI;
}
@ -111,27 +120,37 @@ int ble_hci_trans_ll_evt_tx(uint8_t *hci_ev)
return rc;
}
void ble_hci_trans_hs_acl_tx_on_core_0(void *arg)
{
uint8_t data[MYNEWT_VAL(BLE_ACL_BUF_SIZE) + 1];
struct os_mbuf *om = arg;
uint16_t len = 1 + OS_MBUF_PKTLEN(om);
data[0] = BLE_HCI_UART_H4_ACL;
os_mbuf_copydata(om, 0, OS_MBUF_PKTLEN(om), &data[1]);
esp_vhci_host_send_packet(data, len);
}
int ble_hci_trans_hs_acl_tx(struct os_mbuf *om)
{
uint16_t len = 0;
uint8_t data[MYNEWT_VAL(BLE_ACL_BUF_SIZE) + 1], rc = 0;
uint8_t rc = 0;
/* If this packet is zero length, just free it */
if (OS_MBUF_PKTLEN(om) == 0) {
os_mbuf_free_chain(om);
return 0;
}
data[0] = BLE_HCI_UART_H4_ACL;
len++;
if (!esp_vhci_host_check_send_available()) {
ESP_LOGD(TAG, "Controller not ready to receive packets");
}
os_mbuf_copydata(om, 0, OS_MBUF_PKTLEN(om), &data[1]);
len += OS_MBUF_PKTLEN(om);
if (xSemaphoreTake(vhci_send_sem, NIMBLE_VHCI_TIMEOUT_MS / portTICK_PERIOD_MS) == pdTRUE) {
esp_vhci_host_send_packet(data, len);
if (xPortGetCoreID() != 0) {
esp_ipc_call_blocking(0, ble_hci_trans_hs_acl_tx_on_core_0, om);
} else {
ble_hci_trans_hs_acl_tx_on_core_0(om);
}
} else {
rc = BLE_HS_ETIMEOUT_HCI;
}
@ -451,6 +470,7 @@ esp_err_t esp_nimble_hci_and_controller_init(void)
esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT);
esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
bt_cfg.ble_max_conn = CONFIG_BT_NIMBLE_MAX_CONNECTIONS;
if ((ret = esp_bt_controller_init(&bt_cfg)) != ESP_OK) {
return ret;

View File

@ -53,6 +53,7 @@ struct ble_hs_peer_sec {
*/
struct ble_hs_dev_records {
bool rec_used;
uint8_t rand_addr_type;
uint8_t pseudo_addr[BLE_DEV_ADDR_LEN];
uint8_t rand_addr[BLE_DEV_ADDR_LEN];
uint8_t identity_addr[BLE_DEV_ADDR_LEN];

View File

@ -181,6 +181,44 @@ is_rpa_resolvable_by_peer_rec(struct ble_hs_dev_records *p_dev_rec, uint8_t *pee
return false;
}
static void
ble_rpa_replace_id_with_rand_addr(uint8_t *addr_type, uint8_t *peer_addr)
{
struct ble_hs_dev_records *p_dev_rec;
ble_addr_t p_addr = {0};
struct ble_hs_conn *conn = NULL;
p_dev_rec = ble_rpa_find_peer_dev_rec(peer_addr);
if (p_dev_rec != NULL) {
if (memcmp(p_dev_rec->rand_addr, p_dev_rec->identity_addr, BLE_DEV_ADDR_LEN)) {
/* OTA address (before resolving) gets saved in RAND_ADDR when the peer
* record is fetched from resolving list. Replace peer address
* with rand_addr to maintain status quo for new pairing/encryption request. */
p_addr.type = *addr_type;
memcpy(&p_addr.val[0], peer_addr, BLE_DEV_ADDR_LEN);
ble_hs_lock();
conn = ble_hs_conn_find_by_addr(&p_addr);
/* Rewrite the peer address history in ble_hs_conn. Need to take
* this step to avoid taking wrong address during re-pairing
* process */
if (conn != NULL) {
conn->bhc_peer_rpa_addr.type = p_dev_rec->rand_addr_type;
memcpy(&conn->bhc_peer_rpa_addr.val[0], p_dev_rec->rand_addr, BLE_DEV_ADDR_LEN);
conn->bhc_peer_addr.type = p_dev_rec->rand_addr_type;
memcpy(&conn->bhc_peer_addr.val[0], p_dev_rec->rand_addr, BLE_DEV_ADDR_LEN);
BLE_HS_LOG(DEBUG, "\n Replace Identity addr with random addr received at"
" start of the connection\n");
}
ble_hs_unlock();
}
}
return;
}
/* Add peer to peer device records.
*
* @return 0 if added successfully,
@ -227,6 +265,7 @@ ble_rpa_find_rl_from_peer_records(uint8_t *peer_addr, uint8_t *peer_addr_type)
if (is_rpa_resolvable_by_peer_rec(p_dev_rec, peer_addr)) {
memcpy(p_dev_rec->rand_addr, peer_addr, BLE_DEV_ADDR_LEN);
p_dev_rec->rand_addr_type = *peer_addr_type;
rl = ble_hs_resolv_list_find(p_dev_rec->identity_addr);
if (rl) {
memcpy(peer_addr, p_dev_rec->identity_addr, BLE_DEV_ADDR_LEN);
@ -277,13 +316,10 @@ ble_rpa_replace_peer_params_with_rl(uint8_t *peer_addr, uint8_t *addr_type,
if (is_rpa) {
ble_hs_log_flat_buf(peer_addr, BLE_DEV_ADDR_LEN);
rl_tmp = ble_hs_resolv_list_find(peer_addr);
BLE_HS_LOG(DEBUG, "\n");
/* Try to find from your peer_device records, if RL doesn't
* exist */
if (rl_tmp == NULL) {
rl_tmp = ble_rpa_find_rl_from_peer_records(peer_addr, addr_type);
}
/* Try to find RL from your peer_device records */
rl_tmp = ble_rpa_find_rl_from_peer_records(peer_addr, addr_type);
}
if (rl != NULL) {
@ -571,7 +607,7 @@ ble_hs_resolv_list_add(uint8_t *cmdbuf)
int
ble_hs_resolv_list_rmv(uint8_t addr_type, uint8_t *ident_addr)
{
int position;
int position, rc = BLE_HS_ENOENT;
/* Remove from IRK records */
position = ble_hs_is_on_resolv_list(ident_addr, addr_type);
@ -583,10 +619,15 @@ ble_hs_resolv_list_rmv(uint8_t addr_type, uint8_t *ident_addr)
ble_hs_resolv_entry));
--g_ble_hs_resolv_data.rl_cnt;
return 0;
rc = 0;
}
return BLE_HS_ENOENT;
/* As we are removing the RL record, it is needed to change
* peer_address to its latest received OTA address, this helps when existing bond at
* peer side is removed */
ble_rpa_replace_id_with_rand_addr(&addr_type, ident_addr);
return rc;
}
/**

View File

@ -53,6 +53,7 @@ struct ble_hs_peer_sec {
*/
struct ble_hs_dev_records {
bool rec_used;
uint8_t rand_addr_type;
uint8_t pseudo_addr[BLE_DEV_ADDR_LEN];
uint8_t rand_addr[BLE_DEV_ADDR_LEN];
uint8_t identity_addr[BLE_DEV_ADDR_LEN];

View File

@ -1,12 +1,21 @@
/** @file
*
* IGNORE THIS FILE IF USING ESP-IDF, USE MENUCONFIG TO SET NIMBLE OPTIONS.
*
* The config options here are for Arduino use only.
*/
#pragma once
#include "sdkconfig.h"
/** For ESP-IDF compatibility
*
* Some versions of ESP-IDF used the config name format "CONFIG_NIMBLE_".
* This converts them to "CONFIG_BT_NIMBLE_" format used in the latest IDF.
/*
* For ESP-IDF compatibility
* Some versions of ESP-IDF used the config name format "CONFIG_NIMBLE_".
* This converts them to "CONFIG_BT_NIMBLE_" format used in the latest IDF.
*/
/* Detect if using ESP-IDF or Arduino (Arduino won't have these defines in sdkconfig)*/
/* Detect if using ESP-IDF or Arduino (Arduino won't have these defines in sdkconfig) */
#if defined(CONFIG_BT_NIMBLE_TASK_STACK_SIZE) || defined(CONFIG_NIMBLE_TASK_STACK_SIZE)
#if defined(CONFIG_NIMBLE_ENABLED) && !defined(CONFIG_BT_NIMBLE_ENABLED)
@ -36,99 +45,171 @@
#else // Using Arduino
/***********************************************
* Arduino config options
* Arduino config options start here
**********************************************/
/** Comment out if not using NimBLE Client functions
/** @brief Comment out if not using NimBLE Client functions \n
* Reduces flash size by approx. 7kB.
*/
#define CONFIG_BT_NIMBLE_ROLE_CENTRAL
/** Comment out if not using NimBLE Scan functions
/** @brief Comment out if not using NimBLE Scan functions \n
* Reduces flash size by approx. 26kB.
*/
#define CONFIG_BT_NIMBLE_ROLE_OBSERVER
/** Comment out if not using NimBLE Server functions
/** @brief Comment out if not using NimBLE Server functions \n
* Reduces flash size by approx. 16kB.
*/
// #define CONFIG_BT_NIMBLE_ROLE_PERIPHERAL
/** Comment out if not using NimBLE Advertising functions
/** @brief Comment out if not using NimBLE Advertising functions \n
* Reduces flash size by approx. 5kB.
*/
// #define CONFIG_BT_NIMBLE_ROLE_BROADCASTER
/** Uncomment to see debug log messages from the NimBLE host
/* Uncomment to see debug log messages from the NimBLE host
* Uses approx. 32kB of flash memory.
*/
// #define CONFIG_BT_NIMBLE_DEBUG
/** Uncomment to see NimBLE host return codes as text debug log messages.
/* Uncomment to see NimBLE host return codes as text debug log messages.
* Uses approx. 7kB of flash memory.
*/
// #define CONFIG_NIMBLE_CPP_ENABLE_RETURN_CODE_TEXT
/** Uncomment to see GAP event codes as text in debug log messages.
/* Uncomment to see GAP event codes as text in debug log messages.
* Uses approx. 1kB of flash memory.
*/
// #define CONFIG_NIMBLE_CPP_ENABLE_GAP_EVENT_CODE_TEXT
/** Uncomment to see advertisment types as text while scanning in debug log messages.
/* Uncomment to see advertisment types as text while scanning in debug log messages.
* Uses approx. 250 bytes of flash memory.
*/
// #define CONFIG_NIMBLE_CPP_ENABLE_ADVERTISMENT_TYPE_TEXT
/** Sets the core NimBLE host runs on */
/** @brief Sets the core NimBLE host runs on */
#define CONFIG_BT_NIMBLE_PINNED_TO_CORE 0
/** Sets the stack size for the NimBLE host task */
/** @brief Sets the stack size for the NimBLE host task */
#define CONFIG_BT_NIMBLE_TASK_STACK_SIZE 4096
/** Sets the number of simultaneous connections (esp controller max is 9) */
/**
* @brief Sets the memory pool where NimBLE will be loaded
* @details By default NimBLE is loaded in internal ram.\n
* To use external PSRAM you must change this to `#define CONFIG_BT_NIMBLE_MEM_ALLOC_MODE_EXTERNAL 1`
*/
#define CONFIG_BT_NIMBLE_MEM_ALLOC_MODE_INTERNAL 1
/** @brief Sets the number of simultaneous connections (esp controller max is 9) */
#define CONFIG_BT_NIMBLE_MAX_CONNECTIONS 3
/** Sets the number of devices allowed to store/bond with */
/** @brief Sets the number of devices allowed to store/bond with */
#define CONFIG_BT_NIMBLE_MAX_BONDS 3
/** Sets the number of CCCD's to store per bonded device */
/** @brief Sets the maximum number of CCCD subscriptions to store */
#define CONFIG_BT_NIMBLE_MAX_CCCDS 8
/** @brief Set if CCCD's and bond data should be stored in NVS */
#define CONFIG_BT_NIMBLE_NVS_PERSIST 0
/** @brief Allow legacy paring */
#define CONFIG_BT_NIMBLE_SM_LEGACY 1
/** @brief Allow BLE secure connections */
#define CONFIG_BT_NIMBLE_SM_SC 1
/** @brief Default device name */
#define CONFIG_BT_NIMBLE_SVC_GAP_DEVICE_NAME "nimble"
/** @brief Max device name length (bytes) */
#define CONFIG_BT_NIMBLE_GAP_DEVICE_NAME_MAX_LEN 31
/** @brief Default MTU size */
#define CONFIG_BT_NIMBLE_ATT_PREFERRED_MTU 256
/** @brief Default GAP appearance */
#define CONFIG_BT_NIMBLE_SVC_GAP_APPEARANCE 0x0
/** @brief ACL Buffer count */
#define CONFIG_BT_NIMBLE_ACL_BUF_COUNT 12
/** @brief ACL Buffer size */
#define CONFIG_BT_NIMBLE_ACL_BUF_SIZE 255
/** @brief HCI Event Buffer size */
#define CONFIG_BT_NIMBLE_HCI_EVT_BUF_SIZE 70
/** @brief Number of high priority HCI event buffers */
#define CONFIG_BT_NIMBLE_HCI_EVT_HI_BUF_COUNT 30
/** @brief Number of low priority HCI event buffers */
#define CONFIG_BT_NIMBLE_HCI_EVT_LO_BUF_COUNT 8
/**
* @brief Sets the number of MSYS buffers available.
* @details MSYS is a system level mbuf registry. For prepare write & prepare \n
* responses MBUFs are allocated out of msys_1 pool. This may need to be increased if\n
* you are sending large blocks of data with a low MTU. E.g: 512 bytes with 23 MTU will fail.
*/
#define CONFIG_BT_NIMBLE_MSYS1_BLOCK_COUNT 12
/** @brief Random address refresh time in seconds */
#define CONFIG_BT_NIMBLE_RPA_TIMEOUT 900
/** @brief Maximum number of connection oriented channels */
#define CONFIG_BT_NIMBLE_L2CAP_COC_MAX_NUM 0
/* These should not be altered */
#define CONFIG_BT_NIMBLE_HS_FLOW_CTRL 1
#define CONFIG_BT_NIMBLE_HS_FLOW_CTRL_ITVL 1000
#define CONFIG_BT_NIMBLE_HS_FLOW_CTRL_THRESH 2
#define CONFIG_BT_NIMBLE_HS_FLOW_CTRL_TX_ON_DISCONNECT 1
#define CONFIG_BT_NIMBLE_RPA_TIMEOUT 900
#define CONFIG_BT_NIMBLE_L2CAP_COC_MAX_NUM 0
/** Do not comment out */
#ifndef CONFIG_BT_ENABLED
#define CONFIG_BT_ENABLED
#endif
#define CONFIG_BTDM_CONTROLLER_MODE_BLE_ONLY
#define CONFIG_BT_NIMBLE_MEM_ALLOC_MODE_INTERNAL
#endif // #if defined(CONFIG_BT_NIMBLE_TASK_STACK_SIZE) || defined(CONFIG_NIMBLE_TASK_STACK_SIZE)
/** Cannot use client without scan */
/**********************************
End Arduino config
**********************************/
/* Cannot use client without scan */
#if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL) && !defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
#define CONFIG_BT_NIMBLE_ROLE_OBSERVER
#endif
/** Cannot use server without advertise */
/* Cannot use server without advertise */
#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) && !defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
#define CONFIG_BT_NIMBLE_ROLE_BROADCASTER
#endif
#ifdef _DOXYGEN_
/** @brief Uncomment to see debug log messages from the NimBLE host \n
* Uses approx. 32kB of flash memory.
*/
#define CONFIG_BT_NIMBLE_DEBUG
/** @brief Uncomment to see NimBLE host return codes as text debug log messages. \n
* Uses approx. 7kB of flash memory.
*/
#define CONFIG_NIMBLE_CPP_ENABLE_RETURN_CODE_TEXT
/** @brief Uncomment to see GAP event codes as text in debug log messages. \n
* Uses approx. 1kB of flash memory.
*/
#define CONFIG_NIMBLE_CPP_ENABLE_GAP_EVENT_CODE_TEXT
/** @brief Uncomment to see advertisment types as text while scanning in debug log messages. \n
* Uses approx. 250 bytes of flash memory.
*/
#define CONFIG_NIMBLE_CPP_ENABLE_ADVERTISMENT_TYPE_TEXT
#endif // _DOXYGEN_

View File

@ -346,7 +346,8 @@ os_mempool_info_get_next(struct os_mempool *mp, struct os_mempool_info *omi)
omi->omi_num_blocks = cur->mp_num_blocks;
omi->omi_num_free = cur->mp_num_free;
omi->omi_min_free = cur->mp_min_free;
strncpy(omi->omi_name, cur->name, sizeof(omi->omi_name));
strncpy(omi->omi_name, cur->name, sizeof(omi->omi_name) - 1);
omi->omi_name[sizeof(omi->omi_name) - 1] = '\0';
return (cur);
}