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);
};