53 Commits
1.0.2 ... 1.2.0

Author SHA1 Message Date
h2zero
7bec9c240a Release 1.2.0 2021-02-08 11:46:11 -07:00
h2zero
1210772332 Update changelog. 2021-02-08 10:18:59 -07:00
David Robertson
7dd4d68806 [NimBLEServer] Support duplicate characteristics (#36)
* Add method to get all characteristics of a service

* Added method on NimBLEDescriptor to get the value out as a std::string

* Added methods to get things by handle where UUID could be used already.

* Added helper methods for getting a list of characteristics with a given UUID and service

* Demote the warning log for adding duplicate characteristics to a debug level log.

* Add methods to get services and characteristics using UUID + index
2021-02-08 08:28:32 -07:00
h2zero
28d6492ea4 NimBLEAdvertising::reset check if the stack is initialized before stop.
This prevents an exception when initializing a NimBLEAdvertising instance before calling NimBLEDevice::init().
The constructor calls reset() which calls stop(), if the stack was not yet initialized it will cause an exception.
2021-02-07 22:26:02 -07:00
h2zero
dff5122ce2 Add complementary methods to NimBLEAdvertising and NimBLEAdvertisementData
Previous to this NimBLEAdvertising did not have some functionality that was in NimBLEAdvertisementData.
Also NimBLEAdvertisementData was missing functionality that exists in NimBLEAdvertising.

This resolves that by adding the missing functions to both classes.

Changed:
NimBLEAdvertising: Transmission power is no longer advertised by default and can be added to the advertisement by calling ::addTxPower()

Added:
- NimBLEAdvertising::setName
- NimBLEAdvertising::setManufacturerData
- NimBLEAdvertising::setURI
- NimBLEAdvertising::setServiceData
- NimBLEAdvertising::addTxPower
- NimBLEAdvertising::reset

- NimBLEAdvertisementData::addTxPower
- NimBLEAdvertisementData::setPreferredParams
- NimBLEAdvertisementData::setURI
2021-02-07 20:33:57 -07:00
h2zero
6e104dfb45 Allow custom scan response data without custom advertising.
This lets applications use custom scan response data while using the advertisment data added by the NimBLEAdvertising add/set methods.
2021-02-06 14:29:03 -07:00
h2zero
b290ca9077 [NimBLEAdvertisementData] Add setServices methods for multiple UUID's
Adds:
- setPartialServices16
- setPartialServices32
- setCompleteServices16
- setCompleteServices32

Each takes an input parameter of std::vector<NimBLEUUID> to allow for advertising multiple services with a simple interface.
2021-02-06 13:27:12 -07:00
h2zero
ae2ff3a638 Fix Documentation. 2021-02-06 11:12:40 -07:00
James Fellows
43c9d96373 Update migration guide to include Characteristic constructor now being private 2021-02-03 20:18:44 -07:00
h2zero
af24cd7720 Add controller scan filter options and refactor nimconfig.h
* Add the option to change the scan filter mode through NimBLEDevice::setScanFilterMode

* Add the option to change the scan filter cache size through NimBLEDevice::setScanDuplicateCacheSize

* Refactor nimconfig to make it for doxygen use only.
2021-01-31 21:15:31 -07:00
h2zero
82524c80e3 [NimBLEScan] Use controller duplicate filter and add max result limit.
* Adds setMaxResults to NimBLEScan to limit the number of advertised devices stored.
  If set to 0 no devices will be stored in memory and only the callback will be invoked.
  If set to 0xFF (default) stored devices will be unlimited. Any other value will be the limit.

* Uses the controller to filter duplicate devices from the scan results when wantDuplicates == false.
2021-01-30 18:27:06 -07:00
h2zero
9527c41486 Refactor advertisedDevice (#181)
* Stores complete advertisement payload and only performs parsing on demand. The advertised data is no longer parsed automatically as it is discovered, instead it will be parsed to find only the data requested by the user application when it makes a call to do so. This saves processing time and approximately 2.4k in flash size, with the new methods listed below included.

Add more advertised device field methods:
* Adds haveAdvInterval/getAdvInterval - checks if the interval is advertised / gets the advertisement interval value.

* Adds haveConnParams/getMinInterval/getMaxInterval - checks if the parameters are advertised / get min value / get max value.

* Adds haveURI/getURI - checks if a URI is advertised / gets the URI data.

* Adds haveTargetAddress/getTargetAddressCount/getTargetAddress(index) - checks if a target address is present / gets a count of the addresses targeted / gets the address of the target at index.
2021-01-30 18:06:29 -07:00
wakwak_koba
51c49ba9fc [NimBLEHIDDevice] Modern Apple devices require READ_ENC and WRITE_ENC (#182)
- Added `READ_ENC` to the attribute of characteristics in 1812/2a4d
- Added `READ_ENC` and `WRITE_ENC` to the default values ​​of properties of `createDescriptor()`
2021-01-30 09:04:51 -07:00
h2zero
4e24a06645 Release 1.1.0 2021-01-20 20:18:23 -07:00
h2zero
310c5f7c84 Remove log print in disconnect timer callback.
Timer callback runs in ISR context, so log printing is inappropriate.
2021-01-20 12:29:22 -07:00
h2zero
026864e031 Set Client connect/disconnect data before stack calls.
Connect should set m_pTaskData before calling ble_gap_connect in case of an early event.

Disconnect should check if the timer has already started before starting the timer so it does not reset it.
The timer should also be started before calling ble_gap_terminate in case of an early event that would cause the timer to start
after disconnection, resetting the host unnecessarily.
2021-01-20 12:28:07 -07:00
h2zero
cf64169bc0 Update documentation.
* Docuemnt HID device class.

* Add usage tips.
2021-01-17 17:29:28 -07:00
h2zero
559a6e6970 Update documentation. 2021-01-15 21:51:49 -07:00
h2zero
a4e085f71a Fix compilation when using CMake.
* Update readme to include note about compiling with Arduino component.
2021-01-15 20:16:07 -07:00
h2zero
26ab9760da Update changelog. 2021-01-14 11:40:27 -07:00
h2zero
c089eab595 Check connection status when returning in NimBLEClient::Connect.
There is a chance to become disconnected before returning from the onConnect callback so the connection
status should be checked when returning from client connect.
2021-01-14 10:21:25 -07:00
h2zero
c157680575 Limit delay in NimBLEClient::connect to connection timeout.
In the case the controller become unresponsive, or does not comply with the timeout period,
this will ensure the application continues instead of potentially blocking forever.
2021-01-13 22:00:39 -07:00
h2zero
6ee1cc236a Advertising start: add return status, use more verbose logging.
Advertising start did not return a value to indicate success/failure,
this patch adds that functionality. In addition, more verbose logging of errors from
this function are provided with the removal of the related asserts.

* Minor code cleanup
2021-01-13 19:48:38 -07:00
h2zero
6487225563 Call advertising stopped callback when the host re-synced.
If the stack was reset and duration was not indefinite the callback for
advertising stopped should be called when re-synced.
2021-01-13 18:34:49 -07:00
h2zero
5629f4d3e9 Initialize duration member variable in NimBLEAdvertising constructor.
m_duration member variable was not set previously which could trigger advertising to start if the
host was reset prior to the application calling start.
2021-01-13 18:21:23 -07:00
wakwak_koba
b064cc65d4 Find characteristic end handle using the next characterisic handle.
On some devices, searching for descriptors using the service end handle would return an invalid error.
This patch instead uses the handle of the next characteristic as the end handle (if available).
2021-01-13 18:05:19 -07:00
Asuki Kono
f61bd5c2df Add option to use resolvable and non-resolvable private address.
Adds the possibility to configure a resolvable or non-resolvable address (BLE privacy).
2021-01-13 18:01:41 -07:00
h2zero
57ba0e583d Fix commit 8fe2766 in scan start.
Scan start would return false when the return code was BLE_HS_EALREADY.
2021-01-12 21:25:30 -07:00
h2zero
372c79a6b8 Only start scan/advertise when host re-synced if duration was indefinite.
Previously when the host reset while scanning (if active prior) it would be restarted automatically.
This caused errors for some applications and has been removed since the event invokes the scan
ended callback for the app to take action. Instead scanning will now only be restarted if the duration
was indefinite and a callback was set for the advertisment events, this use case is less likely to have
a scan ended callback.

Advertising (if active prior) would be started without regard to it's previous state.
This has been corrected to only start advertising again if it was advertising for an idefinite time.
2021-01-12 20:42:19 -07:00
h2zero
28573f5abe Fix crash in NimBLEDevice::deleteClient when notification arrives.
While deleting the client attribute database, if a notification occurs there is a possibility of
concurrency causing an exception. This fixes that by setting a flag before calling disconnect in
the deleteClient function to prevent processing further notifications.
2021-01-12 14:01:44 -07:00
h2zero
b807321d1b NimBLEDevice::onSync should call taskYIELD() before returning.
This change is needed to allow any tasks that were stopped during a host reset to finish
before resuming from the re-sync. This would occasionally result in a LoadStoreError exception
if not done.
2021-01-12 13:59:49 -07:00
h2zero
4f4883d6ba Prevent notifications from triggering exception while deleting services.
If we client just connected and a notification comes before deleting services it could cause an exception
when accessing a vector that is being deleted. This will check the connection established flag before
processing of notifications.
2021-01-12 13:58:12 -07:00
h2zero
765d5b1be7 Prioritize processing host reset events in disconnect event handler. 2021-01-12 13:56:29 -07:00
h2zero
09ff0c3472 Remove client delay calls and check if task is valid before notifying. 2021-01-12 13:54:53 -07:00
h2zero
740f280664 Don't call application callbacks invoked when connection not established.
If a connection event was sent but failed to establish then the disconnect
callback would be triggered when the application was not yet informed of the connection.

* Cleanup logs and add comments.
2021-01-12 13:52:28 -07:00
h2zero
28717c300a Fix client connect return code handling, add disconnect timer.
* Handle the return codes from ble_gap_connect to take proper actions for different codes.

* Improve client event handling to accomodate delayed PDU responses.

* Use connection ID as a replacement for the isConnected flag. Also check if a task is waiting for
  results instead of the waitingToConnect flag.

* Adds a disconnect timer so that calling disconnect will start a timer for the connection
  supervision timeout time + a small delay. When this expires it will reset the host if a disconnect event
  does not occur in this time. This is added as a workaround for the occasional situation
  when the controller does not send an event after the disconnect command.
2021-01-12 13:50:08 -07:00
h2zero
8fe2766e01 Remove loop on BLE_HS_EBUSY in NimBLEScan::start
The BLE_HS_EBUSY return code was causing the application to hang when starting scan as
occasionally the code would not change, resulting in an infinite loop.

This patch handles the return code more appopriately and removes the loop.

Additionally a race condition would sometimes allow the code to execute past the conditional checks
and clear the advertised device vector while a scan was active and writing to it causing an exception.
This has been hopefully corrected by only clearing the vector if the return code from starting the scan is 0.
2021-01-10 21:54:32 -07:00
h2zero
a5ad7ff43e Update doxygen version. 2020-12-28 21:35:26 -07:00
h2zero
39a3a63f80 Fix attributes not found with 16/32bit UUIDs.
* Some peripherals will advertise 16/32bit UUIDs but when queried for their handles
they do not convert the UUID to 128 bit locally and will return attribute not found.

This patch will query the peripheral a second time with a 128bit converted UUID.
2020-12-28 15:40:01 -07:00
h2zero
27fc792952 Fix UUID comparison for 16bit + base UUID's
* Add more information to UUID debug messages.
2020-12-28 12:05:54 -07:00
h2zero
ebd7598c49 Re-introduce NimBLEAdvertising::setMin/MaxPreferred.
This implements the functionality of the original library min/max preferred connection parameters settings.
2020-12-27 15:25:38 -07:00
Deepak Singhal
36317e18db Allow subscribing to characteristics if CCCD does not exist on peer device.
- This changes NimBLERemoteCharacteristic::subscribe and NimBLERemoteCharacteristic::registerForNotify functionality
such that the notification callback will always be set.

Additionally these methods will always return true unless the descriptor write fails.

This allows for notifications to trigger the callback when a peer device sends them and does not have a CCCD.
Also it should not be flagged as a failure if the CCCD does not exist.
It should only be flagged when an acutal write operation fails.
2020-12-24 19:59:45 -07:00
h2zero
22d5564d04 Fix compilation errors when using latest Arduino master and IDF 3.3+ 2020-12-24 15:05:32 -07:00
h2zero
d29ad95dfe Rename command line config macros for role disable. 2020-12-20 14:11:19 -07:00
h2zero
de59693f0f Add response parameter to NimBLEClient::setValue 2020-12-20 09:57:26 -07:00
h2zero
77f477f428 Add conditional checks to nimconfig to support command line defines.
* Support platformio config settings on command line.

* Update changelog.
2020-12-19 23:05:54 -07:00
Anthony Doud
7ed962d963 Fix crashing caused by calling time() in a critical section (#159)
* Fix for random notify crash in ISR context

Tested for stability over 30 minutes with a daisy chain of 3 esp32

Co-authored-by: h2zero <powellperalta@gmail.com>
2020-12-19 21:58:37 -07:00
h2zero
a85ac6ad5a Add connection desc param to disconnect callback. 2020-12-04 19:03:52 -07:00
h2zero
3e9a63a514 Correct flags for advertisement type. 2020-11-06 15:44:35 -07:00
wakwak_koba
8d550a6905 Fixed incomplete payload size calculation
* Fixed incomplete payload size calculation

* Disable tx_pwr_lvl when RSP is not possible
2020-10-24 07:57:17 -06:00
wakwak_koba
8e7fcafa9e Add NimBLEHIDDevice class 2020-10-13 19:55:51 -06:00
h2zero
22103af037 Allow scan start from scan complete callback.
* Correct comment in scan start
2020-10-13 19:49:45 -06:00
Guo-Rong Koh
a331cb05e9 NimBLEClient/ Add getCharacteristic() by handle.
* Add getCharacteristic() by handle.

Add a convenience method for getting a characteristic by the 16-bit
handle.
2020-09-23 19:44:11 -06:00
40 changed files with 2665 additions and 1009 deletions

View File

@@ -2,11 +2,11 @@ sudo: false
before_install:
- cd ${TMPDIR-/tmp}
- wget -q http://doxygen.nl/files/doxygen-1.8.19.src.tar.gz
- tar -xzvf doxygen-1.8.19.src.tar.gz
- wget -q http://doxygen.nl/files/doxygen-1.9.0.src.tar.gz
- tar -xzvf doxygen-1.9.0.src.tar.gz
- mkdir doxygen_build
- cd doxygen_build
- cmake ../doxygen-1.8.19/
- cmake ../doxygen-1.9.0/
- make
- export PATH="${TMPDIR-/tmp}/doxygen_build/bin:$PATH"
- cd ${TRAVIS_BUILD_DIR}

View File

@@ -2,6 +2,140 @@
All notable changes to this project will be documented in this file.
## [1.2.0] - 2021-02-08
### Added
- `NimBLECharacteristic::getDescriptorByHandle`: Return the BLE Descriptor for the given handle.
- `NimBLEDescriptor::getStringValue`: Get the value of this descriptor as a string.
- `NimBLEServer::getServiceByHandle`: Get a service by its handle.
- `NimBLEService::getCharacteristicByHandle`: Get a pointer to the characteristic object with the specified handle.
- `NimBLEService::getCharacteristics`: Get the vector containing pointers to each characteristic associated with this service.
Overloads to get a vector containing pointers to all the characteristics in a service with the UUID. (supports multiple same UUID's in a service)
- `NimBLEService::getCharacteristics(const char *uuid)`
- `NimBLEService::getCharacteristics(const NimBLEUUID &uuid)`
- `NimBLEAdvertisementData` New methods:
- `NimBLEAdvertisementData::addTxPower`: Adds transmission power to the advertisement.
- `NimBLEAdvertisementData::setPreferredParams`: Adds connection parameters to the advertisement.
- `NimBLEAdvertisementData::setURI`: Adds URI data to the advertisement.
- `NimBLEAdvertising` New methods:
- `NimBLEAdvertising::setName`: Set the name advertised.
- `NimBLEAdvertising::setManufacturerData`: Adds manufacturer data to the advertisement.
- `NimBLEAdvertising::setURI`: Adds URI data to the advertisement.
- `NimBLEAdvertising::setServiceData`: Adds service data to the advertisement.
- `NimBLEAdvertising::addTxPower`: Adds transmission power to the advertisement.
- `NimBLEAdvertising::reset`: Stops the current advertising and resets the advertising data to the default values.
- `NimBLEDevice::setScanFilterMode`: Set the controller duplicate filter mode for filtering scanned devices.
- `NimBLEDevice::setScanDuplicateCacheSize`: Sets the number of advertisements filtered before the cache is reset.
- `NimBLEScan::setMaxResults`: This allows for setting a maximum number of advertised devices stored in the results vector.
- `NimBLEAdvertisedDevice` New data retrieval methods added:
- `haveAdvInterval/getAdvInterval`: checks if the interval is advertised / gets the advertisement interval value.
- `haveConnParams/getMinInterval/getMaxInterval`: checks if the parameters are advertised / get min value / get max value.
- `haveURI/getURI`: checks if a URI is advertised / gets the URI data.
- `haveTargetAddress/getTargetAddressCount/getTargetAddress(index)`: checks if a target address is present / gets a count of the addresses targeted / gets the address of the target at index.
### Changed
- `nimconfig.h` (Arduino) is now easier to use.
- `NimBLEServer::getServiceByUUID` Now takes an extra parameter of instanceID to support multiple services with the same UUID.
- `NimBLEService::getCharacteristic` Now takes an extra parameter of instanceID to support multiple characteristics with the same UUID.
- `NimBLEAdvertising` Transmission power is no longer advertised by default and can be added to the advertisement by calling `NimBLEAdvertising::addTxPower`
- `NimBLEAdvertising` Custom scan response data can now be used without custom advertisment.
- `NimBLEScan` Now uses the controller duplicate filter.
- `NimBLEAdvertisedDevice` Has been refactored to store the complete advertisement payload and no longer parses the data from each advertisement.
Instead the data will be parsed on-demand when the user application asks for specific data.
### Fixed
- `NimBLEHIDDevice` Characteristics now use encryption, this resolves an issue with communicating with devices requiring encryption for HID devices.
## [1.1.0] - 2021-01-20
### Added
- `NimBLEDevice::setOwnAddrType` added to enable the use of random and random-resolvable addresses, by asukiaaa
- New examples for securing and authenticating client/server connections, by mblasee.
- `NimBLEAdvertising::SetMinPreferred` and `NimBLEAdvertising::SetMinPreferred` re-added.
- Conditional checks added for command line config options in `nimconfig.h` to support custom configuration in platformio.
- `NimBLEClient::setValue` Now takes an extra bool parameter `response` to enable the use of write with response (default = false).
- `NimBLEClient::getCharacteristic(uint16_t handle)` Enabling the use of the characteristic handle to be used to find
the NimBLERemoteCharacteristic object.
- `NimBLEHIDDevice` class added by wakwak-koba.
- `NimBLEServerCallbacks::onDisconnect` overloaded callback added to provide a ble_gap_conn_desc parameter for the application
to obtain information about the disconnected client.
- Conditional checks in `nimconfig.h` for command line defined macros to support platformio config settings.
### Changed
- `NimBLEAdvertising::start` now returns a bool value to indicate success/failure.
- Some asserts were removed in `NimBLEAdvertising::start` and replaced with better return code handling and logging.
- If a host reset event occurs, scanning and advertising will now only be restarted if their previous duration was indefinite.
- `NimBLERemoteCharacteristic::subscribe` and `NimBLERemoteCharacteristic::registerForNotify` will now set the callback
regardless of the existance of the CCCD and return true unless the descriptor write operation failed.
- Advertising tx power level is now sent in the advertisement packet instead of scan response.
- `NimBLEScan` When the scan ends the scan stopped flag is now set before calling the scan complete callback (if used)
this allows the starting of a new scan from the callback function.
### Fixed
- Sometimes `NimBLEClient::connect` would hang on the task block if no event arrived to unblock.
A time limit has been added to timeout appropriately.
- When getting descriptors for a characterisic the end handle of the service was used as a proxy for the characteristic end
handle. This would be rejected by some devices and has been changed to use the next characteristic handle as the end when possible.
- An exception could occur when deleting a client instance if a notification arrived while the attribute vectors were being
deleted. A flag has been added to prevent this.
- An exception could occur after a host reset event when the host re-synced if the tasks that were stopped during the event did
not finish processing. A yield has been added after re-syncing to allow tasks to finish before proceeding.
- Occasionally the controller would fail to send a disconnected event causing the client to indicate it is connected
and would be unable to reconnect. A timer has been added to reset the host/controller if it expires.
- Occasionally the call to start scanning would get stuck in a loop on BLE_HS_EBUSY, this loop has been removed.
- 16bit and 32bit UUID's in some cases were not discovered or compared correctly if the device
advertised them as 16/32bit but resolved them to 128bits. Both are now checked.
- `FreeRTOS` compile errors resolved in latest Ardruino core and IDF v3.3.
- Multiple instances of `time()` called inside critical sections caused sporadic crashes, these have been moved out of critical regions.
- Advertisement type now correctly set when using non-connectable (advertiser only) mode.
- Advertising payload length correction, now accounts for appearance.
- (Arduino) Ensure controller mode is set to BLE Only.
## [1.0.2] - 2020-09-13
### Changed
@@ -15,6 +149,7 @@ Any changes to the controller max connection settings in `sdkconfig.h` will now
- (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

View File

@@ -4,26 +4,54 @@ cmake_minimum_required(VERSION 3.5)
set(SUPPORTED_TARGETS esp32)
idf_component_register(SRCS "src/FreeRTOS.cpp"
"src/NimBLE2904.cpp"
"src/NimBLEAddress.cpp"
"src/NimBLEAdvertisedDevice.cpp"
"src/NimBLEAdvertising.cpp"
"src/NimBLEBeacon.cpp"
"src/NimBLECharacteristic.cpp"
"src/NimBLEClient.cpp"
"src/NimBLEDescriptor.cpp"
"src/NimBLEDevice.cpp"
"src/NimBLEEddystoneTLM.cpp"
"src/NimBLEEddystoneURL.cpp"
"src/NimBLERemoteCharacteristic.cpp"
"src/NimBLERemoteDescriptor.cpp"
"src/NimBLERemoteService.cpp"
"src/NimBLEScan.cpp"
"src/NimBLESecurity.cpp"
"src/NimBLEServer.cpp"
"src/NimBLEService.cpp"
"src/NimBLEUtils.cpp"
"src/NimBLEUUID.cpp"
INCLUDE_DIRS "src"
REQUIRES bt)
set(COMPONENT_SRCS
"src/FreeRTOS.cpp"
"src/NimBLE2904.cpp"
"src/NimBLEAddress.cpp"
"src/NimBLEAdvertisedDevice.cpp"
"src/NimBLEAdvertising.cpp"
"src/NimBLEBeacon.cpp"
"src/NimBLECharacteristic.cpp"
"src/NimBLEClient.cpp"
"src/NimBLEDescriptor.cpp"
"src/NimBLEDevice.cpp"
"src/NimBLEEddystoneTLM.cpp"
"src/NimBLEEddystoneURL.cpp"
"src/NimBLEHIDDevice.cpp"
"src/NimBLERemoteCharacteristic.cpp"
"src/NimBLERemoteDescriptor.cpp"
"src/NimBLERemoteService.cpp"
"src/NimBLEScan.cpp"
"src/NimBLESecurity.cpp"
"src/NimBLEServer.cpp"
"src/NimBLEService.cpp"
"src/NimBLEUtils.cpp"
"src/NimBLEUUID.cpp"
)
set(COMPONENT_ADD_INCLUDEDIRS
src
)
set(COMPONENT_PRIV_REQUIRES
nvs_flash
bt
)
if(COMPONENTS MATCHES "esp-nimble-component")
list(APPEND COMPONENT_PRIV_REQUIRES
esp-nimble-component
)
elseif(COMPONENTS MATCHES "nimble")
list(APPEND COMPONENT_PRIV_REQUIRES
nimble
)
endif()
if(COMPONENTS MATCHES "arduino")
list(APPEND COMPONENT_PRIV_REQUIRES
arduino
)
endif()
register_component()

View File

@@ -1,5 +1,7 @@
[Latest release ![Release Version](https://img.shields.io/github/release/h2zero/esp-nimble-cpp.svg?style=plastic)
![Release Date](https://img.shields.io/github/release-date/h2zero/esp-nimble-cpp.svg?style=plastic)](https://github.com/h2zero/esp-nimble-cpp/releases/latest/)
Need help? Have questions or suggestions? Join the [![Gitter](https://badges.gitter.im/NimBLE-Arduino/community.svg)](https://gitter.im/NimBLE-Arduino/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
<br/>
# esp-nimble-cpp
@@ -17,7 +19,7 @@ to provide improved capabilites and stability over the original.
*Your results may vary*
<br/>
### What is NimBLE?
# What is NimBLE?
NimBLE is a completely open source Bluetooth Low Energy stack produced by [Apache](https://github.com/apache/mynewt-nimble).
It is more suited to resource constrained devices than bluedroid and has now been ported to the ESP32 by Espressif.
<br/>
@@ -34,7 +36,7 @@ Call `NimBLEDevice::init("");` in `app_main`.
<br/>
### ESP-IDF v3.2 & v3.3
The NimBLE component does not come with these versions of IDF.
The NimBLE component does not come with these versions of IDF (now included in 3.3.2 and above).
A backport that works in these versions has been created and is [available here](https://github.com/h2zero/esp-nimble-component).
Download or clone that repo into your project/components folder and run menuconfig.
Configure settings in `main menu -> NimBLE Options`.
@@ -55,6 +57,11 @@ Also see [Improvements_and_updates](docs/Improvements_and_updates.md) for inform
[Full API documentation and class list can be found here.](https://h2zero.github.io/esp-nimble-cpp/)
<br/>
## Using with Arduino as an IDF component and CMake
When using this library along with Arduino and compiling with *CMake* you must add `add_compile_definitions(ARDUINO_ARCH_ESP32=1)`
in your project/CMakeLists.txt after the line `include($ENV{IDF_PATH}/tools/cmake/project.cmake)` to prevent Arduino from releasing BLE memory.
<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.

117
docs/Command_line_config.md Normal file
View File

@@ -0,0 +1,117 @@
# Arduino command line and platformio config options
`CONFIG_BT_NIMBLE_MAX_CONNECTIONS`
Sets the number of simultaneous connections (esp controller max is 9)
- Default value is 3
<br/>
`CONFIG_BT_NIMBLE_ATT_PREFERRED_MTU`
Sets the default MTU size.
- Default value is 255
<br/>
`CONFIG_BT_NIMBLE_SVC_GAP_DEVICE_NAME`
Set the default device name
- Default value is "nimble"
<br/>
`CONFIG_BT_NIMBLE_DEBUG`
If defined, enables debug log messages from the NimBLE host
- Uses approx. 32kB of flash memory.
<br/>
`CONFIG_NIMBLE_CPP_ENABLE_RETURN_CODE_TEXT`
If defined, NimBLE host return codes will be printed as text in debug log messages.
- Uses approx. 7kB of flash memory.
<br/>
`CONFIG_NIMBLE_CPP_ENABLE_GAP_EVENT_CODE_TEXT`
If defined, GAP event codes will be printed as text in debug log messages.
- Uses approx. 1kB of flash memory.
<br/>
`CONFIG_NIMBLE_CPP_ENABLE_ADVERTISMENT_TYPE_TEXT`
If defined, advertisment types will be printed as text while scanning in debug log messages.
- Uses approx. 250 bytes of flash memory.
<br/>
`CONFIG_BT_NIMBLE_SVC_GAP_APPEARANCE`
Set the default appearance.
- Default value is 0x00
<br/>
`CONFIG_BT_NIMBLE_ROLE_CENTRAL_DISABLED`
If defined, NimBLE Client functions will not be included.
- Reduces flash size by approx. 7kB.
<br/>
`CONFIG_BT_NIMBLE_ROLE_OBSERVER_DISABLED`
If defined, NimBLE Scan functions will not be included.
- Reduces flash size by approx. 26kB.
<br/>
`CONFIG_BT_NIMBLE_ROLE_PERIPHERAL_DISABLED`
If defined NimBLE Server functions will not be included.
- Reduces flash size by approx. 16kB.
<br/>
`CONFIG_BT_NIMBLE_ROLE_BROADCASTER_DISABLED`
If defined, NimBLE Advertising functions will not be included.
- Reduces flash size by approx. 5kB.
<br/>
`CONFIG_BT_NIMBLE_MAX_BONDS`
Sets the number of devices allowed to store/bond with
- Default value is 3
<br/>
`CONFIG_BT_NIMBLE_MAX_CCCDS`
Sets the maximum number of CCCD subscriptions to store
- Default value is 8
<br/>
`CONFIG_BT_NIMBLE_RPA_TIMEOUT`
Sets the random address refresh time in seconds.
- Default value is 900
<br/>
`CONFIG_BT_NIMBLE_MSYS1_BLOCK_COUNT`
Set the number of msys blocks For prepare write & prepare responses. This may need to be increased if
you are sending large blocks of data with a low MTU. E.g: 512 bytes with 23 MTU will fail.
- Default value is 12
<br/>
`CONFIG_BT_NIMBLE_MEM_ALLOC_MODE_EXTERNAL`
Sets the NimBLE stack to use external PSRAM will be loaded
- Must be defined with a value of 1; Default is CONFIG_BT_NIMBLE_MEM_ALLOC_MODE_INTERNAL 1
<br/>
`CONFIG_BT_NIMBLE_PINNED_TO_CORE`
Sets the core the NimBLE host stack will run on
- Options: 0 or 1
<br/>
`CONFIG_BT_NIMBLE_TASK_STACK_SIZE`
Set the task stack size for the NimBLE core.
- Default is 4096
<br/>

View File

@@ -38,7 +38,7 @@ PROJECT_NAME = "esp-nimble-cpp / NimBLE-Arduino"
# could be handy for archiving the generated documentation or if some version
# control system is used.
PROJECT_NUMBER = 1.0.2
PROJECT_NUMBER = 1.2.0
# Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer a
@@ -2179,8 +2179,12 @@ INCLUDE_FILE_PATTERNS =
# recursively expanded use the := operator instead of the = operator.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
PREDEFINED = CONFIG_BT_ENABLED \
_DOXYGEN_
PREDEFINED = _DOXYGEN_ \
CONFIG_BT_ENABLED \
CONFIG_BT_NIMBLE_ROLE_CENTRAL \
CONFIG_BT_NIMBLE_ROLE_OBSERVER \
CONFIG_BT_NIMBLE_ROLE_PERIPHERAL \
CONFIG_BT_NIMBLE_ROLE_BROADCASTER
# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
# tag can be used to specify a list of macro names that should be expanded. The

View File

@@ -69,7 +69,9 @@ Now takes 2 optional parameters, the first is the duration to advertise for (in
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/>
Also now returns a bool value to indicate if advertising successfully started or not.
<br/>
<a name="client"></a>
# Client
@@ -100,8 +102,18 @@ Has been **deprecated** as now the internally stored characteristic value is upd
`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.
<br/>
In addition `NimBLERemoteCharacteristic::readValue` and `NimBLERemoteCharacteristic::getValue` take an optional timestamp parameter which will update it's value with
The `notifiy_callback` function is now defined as a `std::function` to take advantage of using `std::bind` to specifiy a class member function for the callback.
Example:
```
using namespace std::placeholders;
notify_callback callback = std::bind(&<ClassName>::<memberFunctionCallbackName>, this, _1, _2, _3, _4);
<remoteCharacteristicInstance>->subscribe(true, callback);
```
`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

View File

@@ -69,6 +69,8 @@ For example `BLEServer::createService(SERVICE_UUID)` will work just as it did be
<a name="characteristics"></a>
### Characteristics
The constructor for `(Nim)BLECharacteristic` is now private, so if you currently subclass it to add logic you should switch to use `NimBLEService::createCharacteristic` instead. Any custom processing logic previously in a `BLECharacteristic` subclass should be moved to a `NimBLECharacteristicCallbacks` subclass instead, and passed into `NimBLECharacteristic::setCallbacks`.
`BLEService::createCharacteristic` (`NimBLEService::createCharacteristic`) is used the same way as originally except the properties parameter has changed.
When creating a characteristic the properties are now set with `NIMBLE_PROPERTY::XXXX` instead of `BLECharacteristic::XXXX`.
@@ -218,10 +220,11 @@ If you wish to advertise these parameters you can still do so manually via `BLEA
<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.
`NimBLEAdvertising::setAppearance` or similar methods. 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.
~~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.~~
No longer true as of release 1.2.0 and above, custom scan response is now supported without custom advertisement data.
<br/>
> BLEAdvertising::start (NimBLEAdvertising::start)

41
docs/Usage_tips.md Normal file
View File

@@ -0,0 +1,41 @@
# Usage Tips
## Put BLE functions in a task running on the NimBLE stack core
When commands are sent to the stack from a differnt core they can experience delays in execution.
This library detects this and invokes the esp32 IPC to reroute these commands through the correct core but this also increases overhead.
Therefore it is highly recommended to create tasks for BLE to run on the same core, the macro `CONFIG_BT_NIMBLE_PINNED_TO_CORE` can be used to set the core.
<br/>
## Do not delete client instances unless necessary or unused
When a client instance has been created and has connected to a peer device and it has retrieved service/characteristic information it will store that data for the life of the client instance.
If you are periodically connecting to the same devices and you have deleted the client instance or the services when connecting again it will cause a retrieval of that information from the peer again.
This results in significant energy drain on the battery of the devices, fragments heap, and reduces connection performance.
Client instances in this library use approximately 20% of the original bluedroid library, deleteing them will provide much less gain than it did before.
It is recommended to retain the client instance in cases where the time between connecting to the same device is less than 5 minutes.
<br/>
## Only retrieve the services and characteriscs needed
As a client the use of `NimBLEClient::getServices` or `NimBLERemoteService::getCharacteristics` and using `true` for the parameter should be limited to devices that are not known.
Instead `NimBLEClient::getService(NimBLEUUID)` or `NimBLERemoteService::getCharacteristic(NimBLEUUID)` should be used to access certain attributes that are useful to the application.
This reduces energy consumed, heap allocated, connection time and improves overall efficiency.
<br/>
## Check return values
Many user issues can be avoided by checking if a function returned successfully, by either testing for true/false such as when calling `NimBLEClient::connect`,
or nullptr such as when calling `NimBLEClient::getService`. The latter being a must, as calling a method on a nullptr will surely result in a crash.
Most of the functions in this library return something that should be checked before proceeding.
<br/>
## There will be bugs - please report them
No code is bug free and unit testing will not find them all on it's own. If you encounter a bug, please report it along with any logs and decoded backtrace if applicable.
Best efforts will be made to correct any errors ASAP.
Bug reports can be made at https://github.com/h2zero/NimBLE-Arduino/issues or https://github.com/h2zero/esp-nimble-cpp/issues.
Questions and suggestions will be happily accepted there as well.

View File

@@ -1,24 +1,28 @@
# Overview
This is a C++ BLE library for the ESP32 that uses the NimBLE host stack instead of bluedroid.
The aim is to maintain, as much as reasonable, the original bluedroid C++ API while adding new features
and making improvements in performance, resource use and stability.
The aim is to maintain, as much as reasonable, the original bluedroid C++ & Arduino BLE API by while adding new features
and making improvements in performance, resource use, and stability.
**Testing shows a nearly 50% reduction in flash use and approx. 100kB less ram consumed vs the original!**
*Your results may vary*
<br/>
*Your results may vary*
<br/>
### What is NimBLE?
# What is NimBLE?
NimBLE is a completely open source Bluetooth Low Energy stack produced by [Apache](https://github.com/apache/mynewt-nimble).
It is more suited to resource constrained devices than bluedroid and has now been ported to the ESP32 by Espressif.
<br/>
# Arduino Installation
Download as .zip and extract to Arduino/libraries folder, or in Arduino IDE from Sketch menu -> Include library -> Add .Zip library.
**Arduino Library manager:** Go to `sketch` -> `Include Library` -> `Manage Libraries` and search for NimBLE and install.
`#include "NimBLEDevice.h"` at the beginning of your sketch.
**Alternatively:** Download as .zip and extract to Arduino/libraries folder, or in Arduino IDE from Sketch menu -> Include library -> Add .Zip library.
Tested and working with esp32-arduino Arduino IDE and platform IO.
`#include "NimBLEDevice.h"` at the beginning of your sketch.
Call `NimBLEDevice::init` in `setup`.
Tested and working with esp32-arduino in Arduino IDE and platform IO.
<br/>
# ESP-IDF Installation
@@ -28,17 +32,17 @@ Download as .zip and extract or clone into the components folder in your esp-idf
Run menuconfig, go to `Component config->Bluetooth` enable Bluetooth and in `Bluetooth host` NimBLE.
Configure settings in `NimBLE Options`.
`#include "NimBLEDevice.h"` in main.cpp.
Call `NimBLEDevice::init("");` in `app_main`.
Call `NimBLEDevice::init` in `app_main`.
<br/>
### v3.2 & v3.3
The NimBLE component does not come with these versions of IDF.
The NimBLE component does not come with these versions of IDF (now included in 3.3.2 and above).
A backport that works in these versions has been created and is [available here](https://github.com/h2zero/esp-nimble-component).
Download or clone that repo into your project/components folder and run menuconfig.
Configure settings in `main menu -> NimBLE Options`.
`#include "NimBLEDevice.h"` in main.cpp.
Call `NimBLEDevice::init("");` in `app_main`.
Call `NimBLEDevice::init` in `app_main`.
<br/>
# Using
@@ -48,33 +52,30 @@ If you have not used the original Bluedroid library please refer to the [New use
If you are familiar with the original library, see: [The migration guide](Migration_guide.md) for details.
Also see [Improvements_and_updates](Improvements_and_updates.md) for information about non-breaking changes.
Also see [Improvements and updates](Improvements_and_updates.md) for information about non-breaking changes.
### Arduino specific:
For more advanced usage see [Usage tips](Usage_tips.md) for more performance and optimization.
<br/>
### Arduino specific
See the Refactored_original_examples in the examples folder for highlights of the differences with the original library.
More advanced examples highlighting many available features are in examples/NimBLE_Server, NimBLE_Client.
Beacon examples provided by [beegee-tokyo](https://github.com/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 == 3).
Change the settings in the nimconfig.h file to customize NimBLE to your project, such as increasing max connections (default is 3).
<br/>
**Note To increase max connections in Arduino it is also required to change the controller max connections defined in sdkconfig.h.**
This is located in your Arduino/hardware/espressif/esp32/tools/sdk/include/config folder.
The values in `sdkconfig.h` you will need to change are:
```
#define CONFIG_BTDM_CONTROLLER_BLE_MAX_CONN 3
#define CONFIG_BTDM_CONTROLLER_BLE_MAX_CONN_EFF 3
```
In `nimconfig.h` the value is:
```
#define CONFIG_BT_NIMBLE_MAX_CONNECTIONS 3
```
Espressif has stated the hard maximum connections is 9.
### Arduino command line and platformio
As an alternative to changing the configuration in nimconfig.h, Arduino command line and platformio.ini options are available.
See the command line configuration options available in [Command line config](Command_line_config.md).
<br/>
# Need help? Have a question or suggestion?
Come chat on [gitter](https://gitter.im/NimBLE-Arduino/community?utm_source=share-link&utm_medium=link&utm_campaign=share-link) or open an issue at [NimBLE-Arduino](https://github.com/h2zero/NimBLE-Arduino/issues) or [esp-nimble-cpp](https://github.com/h2zero/esp-nimble-cpp/issues)
<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.

View File

@@ -265,9 +265,13 @@ void FreeRTOS::Semaphore::setName(std::string name) {
* @param [in] type The type of buffer. One of RINGBUF_TYPE_NOSPLIT, RINGBUF_TYPE_ALLOWSPLIT, RINGBUF_TYPE_BYTEBUF.
*/
#ifdef ESP_IDF_VERSION //Quick hack to detect if using IDF version that replaced ringbuf_type_t
Ringbuffer::Ringbuffer(size_t length, RingbufferType_t type) {
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 0, 0)
Ringbuffer::Ringbuffer(size_t length, RingbufferType_t type) {
#else
Ringbuffer::Ringbuffer(size_t length, ringbuf_type_t type) {
Ringbuffer::Ringbuffer(size_t length, ringbuf_type_t type) {
#endif
#else
Ringbuffer::Ringbuffer(size_t length, ringbuf_type_t type) {
#endif
m_handle = ::xRingbufferCreate(length, type);
} // Ringbuffer

View File

@@ -69,7 +69,11 @@ public:
class Ringbuffer {
public:
#ifdef ESP_IDF_VERSION //Quick hack to detect if using IDF version that replaced ringbuf_type_t
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 0, 0)
Ringbuffer(size_t length, RingbufferType_t type = RINGBUF_TYPE_NOSPLIT);
#else
Ringbuffer(size_t length, ringbuf_type_t type = RINGBUF_TYPE_NOSPLIT);
#endif
#else
Ringbuffer(size_t length, ringbuf_type_t type = RINGBUF_TYPE_NOSPLIT);
#endif

View File

@@ -37,7 +37,7 @@ NimBLE2904::NimBLE2904(NimBLECharacteristic* pCharacterisitic)
m_data.m_unit = 0;
m_data.m_description = 0;
setValue((uint8_t*) &m_data, sizeof(m_data));
} // BLE2902
} // BLE2904
/**

View File

@@ -28,25 +28,14 @@ static const char* LOG_TAG = "NimBLEAdvertisedDevice";
/**
* @brief Constructor
*/
NimBLEAdvertisedDevice::NimBLEAdvertisedDevice() {
NimBLEAdvertisedDevice::NimBLEAdvertisedDevice() :
m_payload(62,0)
{
m_advType = 0;
m_appearance = 0;
m_manufacturerData = "";
m_name = "";
m_rssi = -9999;
m_txPower = 0;
m_payloadLength = 0;
m_payload = nullptr;
m_haveAppearance = false;
m_haveManufacturerData = false;
m_haveName = false;
m_haveRSSI = false;
m_haveServiceData = false;
m_haveServiceUUID = false;
m_haveTXPower = false;
m_callbackSent = false;
m_callbackSent = false;
m_timestamp = 0;
m_advLength = 0;
} // NimBLEAdvertisedDevice
@@ -82,25 +71,126 @@ uint8_t NimBLEAdvertisedDevice::getAdvType() {
* @return The appearance of the advertised device.
*/
uint16_t NimBLEAdvertisedDevice::getAppearance() {
return m_appearance;
uint8_t data_loc = 0;
if(findAdvField(BLE_HS_ADV_TYPE_APPEARANCE, 0, &data_loc) > 0) {
ble_hs_adv_field *field = (ble_hs_adv_field *)&m_payload[data_loc];
if(field->length == BLE_HS_ADV_APPEARANCE_LEN + 1) {
return *field->value | *(field->value + 1) << 8;
}
}
return 0;
} // getAppearance
/**
* @brief Get the advertisement interval.
* @return The advertisement interval in 0.625ms units.
*/
uint16_t NimBLEAdvertisedDevice::getAdvInterval() {
uint8_t data_loc = 0;
if(findAdvField(BLE_HS_ADV_TYPE_ADV_ITVL, 0, &data_loc) > 0) {
ble_hs_adv_field *field = (ble_hs_adv_field *)&m_payload[data_loc];
if(field->length == BLE_HS_ADV_ADV_ITVL_LEN + 1) {
return *field->value | *(field->value + 1) << 8;
}
}
return 0;
} // getAdvInterval
/**
* @brief Get the preferred min connection interval.
* @return The preferred min connection interval in 1.25ms units.
*/
uint16_t NimBLEAdvertisedDevice::getMinInterval() {
uint8_t data_loc = 0;
if(findAdvField(BLE_HS_ADV_TYPE_SLAVE_ITVL_RANGE, 0, &data_loc) > 0) {
ble_hs_adv_field *field = (ble_hs_adv_field *)&m_payload[data_loc];
if(field->length == BLE_HS_ADV_SLAVE_ITVL_RANGE_LEN + 1) {
return *field->value | *(field->value + 1) << 8;
}
}
return 0;
} // getMinInterval
/**
* @brief Get the preferred max connection interval.
* @return The preferred max connection interval in 1.25ms units.
*/
uint16_t NimBLEAdvertisedDevice::getMaxInterval() {
uint8_t data_loc = 0;
if(findAdvField(BLE_HS_ADV_TYPE_SLAVE_ITVL_RANGE, 0, &data_loc) > 0) {
ble_hs_adv_field *field = (ble_hs_adv_field *)&m_payload[data_loc];
if(field->length == BLE_HS_ADV_SLAVE_ITVL_RANGE_LEN + 1) {
return *(field->value + 2) | *(field->value + 3) << 8;
}
}
return 0;
} // getMaxInterval
/**
* @brief Get the manufacturer data.
* @return The manufacturer data of the advertised device.
*/
std::string NimBLEAdvertisedDevice::getManufacturerData() {
return m_manufacturerData;
uint8_t data_loc = 0;
if(findAdvField(BLE_HS_ADV_TYPE_MFG_DATA, 0, &data_loc) > 0) {
ble_hs_adv_field *field = (ble_hs_adv_field *)&m_payload[data_loc];
if(field->length > 1) {
return std::string((char*)field->value, field->length - 1);
}
}
return "";
} // getManufacturerData
/**
* @brief Get the URI from the advertisement.
* @return The URI data.
*/
std::string NimBLEAdvertisedDevice::getURI() {
uint8_t data_loc = 0;
if(findAdvField(BLE_HS_ADV_TYPE_URI, 0, &data_loc) > 0) {
ble_hs_adv_field *field = (ble_hs_adv_field *)&m_payload[data_loc];
if(field->length > 1) {
return std::string((char*)field->value, field->length - 1);
}
}
return "";
} // getURI
/**
* @brief Get the advertised name.
* @return The name of the advertised device.
*/
std::string NimBLEAdvertisedDevice::getName() {
return m_name;
uint8_t data_loc = 0;
if(findAdvField(BLE_HS_ADV_TYPE_COMP_NAME, 0, &data_loc) > 0 ||
findAdvField(BLE_HS_ADV_TYPE_INCOMP_NAME, 0, &data_loc) > 0)
{
ble_hs_adv_field *field = (ble_hs_adv_field *)&m_payload[data_loc];
if(field->length > 1) {
return std::string((char*)field->value, field->length - 1);
}
}
return "";
} // getName
@@ -122,17 +212,70 @@ NimBLEScan* NimBLEAdvertisedDevice::getScan() {
} // getScan
/**
* @brief Get the number of target addresses.
* @return The number of addresses.
*/
size_t NimBLEAdvertisedDevice::getTargetAddressCount() {
uint8_t count = 0;
count = findAdvField(BLE_HS_ADV_TYPE_PUBLIC_TGT_ADDR);
count += findAdvField(BLE_HS_ADV_TYPE_RANDOM_TGT_ADDR);
return count;
}
/**
* @brief Get the target address at the index.
* @param [in] index The index of the target address.
* @return The target address.
*/
NimBLEAddress NimBLEAdvertisedDevice::getTargetAddress(uint8_t index) {
ble_hs_adv_field *field = nullptr;
uint8_t count = 0;
uint8_t data_loc = 0xFF;
index++;
count = findAdvField(BLE_HS_ADV_TYPE_PUBLIC_TGT_ADDR, index, &data_loc);
if (count < index) {
index -= count;
count = findAdvField(BLE_HS_ADV_TYPE_RANDOM_TGT_ADDR, index, &data_loc);
}
if(count > 0 && data_loc != 0xFF) {
field = (ble_hs_adv_field *)&m_payload[data_loc];
if(field->length < index * BLE_HS_ADV_PUBLIC_TGT_ADDR_ENTRY_LEN) {
index -= count - field->length / BLE_HS_ADV_PUBLIC_TGT_ADDR_ENTRY_LEN;
}
if(field->length > index * BLE_HS_ADV_PUBLIC_TGT_ADDR_ENTRY_LEN) {
return NimBLEAddress(field->value + (index - 1) * BLE_HS_ADV_PUBLIC_TGT_ADDR_ENTRY_LEN);
}
}
return NimBLEAddress("");
}
/**
* @brief Get the service data.
* @param [in] index The vector index of the service data requested.
* @param [in] index The index of the service data requested.
* @return The advertised service data or empty string if no data.
*/
std::string NimBLEAdvertisedDevice::getServiceData(uint8_t index) {
if(index > m_serviceDataVec.size()) {
NIMBLE_LOGW(LOG_TAG, "getServiceData: index out of range");
return "";
ble_hs_adv_field *field = nullptr;
uint8_t bytes;
uint8_t data_loc = findServiceData(index, &bytes);
if(data_loc != 0xFF) {
field = (ble_hs_adv_field *)&m_payload[data_loc];
if(field->length > bytes) {
return std::string((char*)(field->value + bytes), field->length - bytes - 1);
}
}
return m_serviceDataVec[index].second;
return "";
} //getServiceData
@@ -141,51 +284,148 @@ std::string NimBLEAdvertisedDevice::getServiceData(uint8_t index) {
* @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;
std::string NimBLEAdvertisedDevice::getServiceData(const NimBLEUUID &uuid) {
ble_hs_adv_field *field = nullptr;
uint8_t bytes;
uint8_t index = 0;
uint8_t data_loc = findServiceData(index, &bytes);
uint8_t uuidBytes = uuid.bitSize() / 8;
uint8_t plSize = m_payload.size() - 2;
while(data_loc < plSize) {
field = (ble_hs_adv_field *)&m_payload[data_loc];
if(bytes == uuidBytes && NimBLEUUID(field->value, bytes, false) == uuid) {
return std::string((char*)(field->value + bytes), field->length - bytes - 1);
}
index++;
data_loc = findServiceData(index, &bytes);
}
NIMBLE_LOGW(LOG_TAG, "getServiceData: uuid not found");
NIMBLE_LOGI(LOG_TAG, "No service data found");
return "";
} //getServiceData
/**
* @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.
* @brief Get the UUID of the serice data at the index.
* @param [in] index The index of the service data UUID requested.
* @return The advertised service data 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("");
ble_hs_adv_field *field = nullptr;
uint8_t bytes;
uint8_t data_loc = findServiceData(index, &bytes);
if(data_loc != 0xFF) {
field = (ble_hs_adv_field *)&m_payload[data_loc];
if(field->length >= bytes) {
return NimBLEUUID(field->value, bytes, false);
}
}
return m_serviceDataVec[index].first;
return NimBLEUUID("");
} // getServiceDataUUID
/**
* @brief Find the service data at the index.
* @param [in] index The index of the service data to find.
* @param [in] bytes A pointer to storage for the number of the bytes in the UUID.
* @return The index in the vector where the data is located, 0xFF if not found.
*/
uint8_t NimBLEAdvertisedDevice::findServiceData(uint8_t index, uint8_t *bytes) {
uint8_t data_loc = 0;
uint8_t found = 0;
*bytes = 0;
index++;
found = findAdvField(BLE_HS_ADV_TYPE_SVC_DATA_UUID16, index, &data_loc);
if(found == index) {
*bytes = 2;
return data_loc;
}
index -= found;
found = findAdvField(BLE_HS_ADV_TYPE_SVC_DATA_UUID32, index, &data_loc);
if(found == index) {
*bytes = 4;
return data_loc;
}
index -= found;
found = findAdvField(BLE_HS_ADV_TYPE_SVC_DATA_UUID128, index, &data_loc);
if(found == index) {
*bytes = 16;
return data_loc;
}
return 0xFF;
}
/**
* @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();
uint8_t count = 0;
count += findAdvField(BLE_HS_ADV_TYPE_SVC_DATA_UUID16);
count += findAdvField(BLE_HS_ADV_TYPE_SVC_DATA_UUID32);
count += findAdvField(BLE_HS_ADV_TYPE_SVC_DATA_UUID128);
return count;
} // getServiceDataCount
/**
* @brief Get the Service UUID.
* @param [in] index The vector index of the service UUID requested.
* @param [in] index The 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("");
uint8_t count = 0;
uint8_t data_loc = 0;
uint8_t uuidBytes = 0;
uint8_t type = BLE_HS_ADV_TYPE_INCOMP_UUIDS16;
ble_hs_adv_field *field = nullptr;
index++;
do {
count = findAdvField(type, index, &data_loc);
if(count >= index) {
if(type < BLE_HS_ADV_TYPE_INCOMP_UUIDS32) {
uuidBytes = 2;
} else if(type < BLE_HS_ADV_TYPE_INCOMP_UUIDS128) {
uuidBytes = 4;
} else {
uuidBytes = 16;
}
break;
} else {
type++;
index -= count;
}
} while(type <= BLE_HS_ADV_TYPE_COMP_UUIDS128);
if(uuidBytes > 0) {
field = (ble_hs_adv_field *)&m_payload[data_loc];
// In the case of more than one field of service uuid's we need to adjust
// the index to account for the uuids of the previous fields.
if(field->length < index * uuidBytes) {
index -= count - field->length / uuidBytes;
}
if(field->length > uuidBytes * index) {
return NimBLEUUID(field->value + uuidBytes * (index - 1), uuidBytes, false);
}
}
return m_serviceUUIDs[index];
return NimBLEUUID("");
} // getServiceUUID
@@ -194,18 +434,32 @@ NimBLEUUID NimBLEAdvertisedDevice::getServiceUUID(uint8_t index) {
* @return The count of services in the advertising packet.
*/
size_t NimBLEAdvertisedDevice::getServiceUUIDCount() {
return m_serviceUUIDs.size();
uint8_t count = 0;
count += findAdvField(BLE_HS_ADV_TYPE_INCOMP_UUIDS16);
count += findAdvField(BLE_HS_ADV_TYPE_COMP_UUIDS16);
count += findAdvField(BLE_HS_ADV_TYPE_INCOMP_UUIDS32);
count += findAdvField(BLE_HS_ADV_TYPE_COMP_UUIDS32);
count += findAdvField(BLE_HS_ADV_TYPE_INCOMP_UUIDS128);
count += findAdvField(BLE_HS_ADV_TYPE_COMP_UUIDS128);
return count;
} // getServiceUUIDCount
/**
* @brief Check advertised services for existance of the required UUID
* @param [in] uuid The service uuid to look for in the advertisement.
* @return Return true if service is advertised
*/
bool NimBLEAdvertisedDevice::isAdvertisingService(const NimBLEUUID &uuid) const {
for (int i = 0; i < m_serviceUUIDs.size(); i++) {
if (m_serviceUUIDs[i].equals(uuid)) return true;
bool NimBLEAdvertisedDevice::isAdvertisingService(const NimBLEUUID &uuid) {
size_t count = getServiceUUIDCount();
for(size_t i = 0; i < count; i++) {
if(uuid == getServiceUUID(i)) {
return true;
}
}
return false;
} // isAdvertisingService
@@ -215,16 +469,43 @@ bool NimBLEAdvertisedDevice::isAdvertisingService(const NimBLEUUID &uuid) const
* @return The TX Power of the advertised device.
*/
int8_t NimBLEAdvertisedDevice::getTXPower() {
return m_txPower;
uint8_t data_loc = 0;
if(findAdvField(BLE_HS_ADV_TYPE_TX_PWR_LVL, 0, &data_loc) > 0) {
ble_hs_adv_field *field = (ble_hs_adv_field *)&m_payload[data_loc];
if(field->length == BLE_HS_ADV_TX_PWR_LVL_LEN + 1) {
return *(int8_t*)field->value;
}
}
return -99;
} // getTXPower
/**
* @brief Does this advertisement have preferred connection parameters?
* @return True if connection parameters are present.
*/
bool NimBLEAdvertisedDevice::haveConnParams() {
return findAdvField(BLE_HS_ADV_TYPE_SLAVE_ITVL_RANGE) > 0;
} // haveConnParams
/**
* @brief Does this advertisement have have the advertising interval?
* @return True if the advertisement interval is present.
*/
bool NimBLEAdvertisedDevice::haveAdvInterval() {
return findAdvField(BLE_HS_ADV_TYPE_ADV_ITVL) > 0;
} // haveAdvInterval
/**
* @brief Does this advertisement have an appearance value?
* @return True if there is an appearance value present.
*/
bool NimBLEAdvertisedDevice::haveAppearance() {
return m_haveAppearance;
return findAdvField(BLE_HS_ADV_TYPE_APPEARANCE) > 0;
} // haveAppearance
@@ -233,16 +514,36 @@ bool NimBLEAdvertisedDevice::haveAppearance() {
* @return True if there is manufacturer data present.
*/
bool NimBLEAdvertisedDevice::haveManufacturerData() {
return m_haveManufacturerData;
return findAdvField(BLE_HS_ADV_TYPE_MFG_DATA) > 0;
} // haveManufacturerData
/**
* @brief Does this advertisement have a URI?
* @return True if there is a URI present.
*/
bool NimBLEAdvertisedDevice::haveURI() {
return findAdvField(BLE_HS_ADV_TYPE_URI) > 0;
} // haveURI
/**
* @brief Does the advertisement contain a target address?
* @return True if an address is present.
*/
bool NimBLEAdvertisedDevice::haveTargetAddress() {
return findAdvField(BLE_HS_ADV_TYPE_PUBLIC_TGT_ADDR) > 0 ||
findAdvField(BLE_HS_ADV_TYPE_RANDOM_TGT_ADDR) > 0;
}
/**
* @brief Does this advertisement have a name value?
* @return True if there is a name value present.
*/
bool NimBLEAdvertisedDevice::haveName() {
return m_haveName;
return findAdvField(BLE_HS_ADV_TYPE_COMP_NAME) > 0 ||
findAdvField(BLE_HS_ADV_TYPE_INCOMP_NAME) > 0;
} // haveName
@@ -251,7 +552,7 @@ bool NimBLEAdvertisedDevice::haveName() {
* @return True if there is a signal strength value present.
*/
bool NimBLEAdvertisedDevice::haveRSSI() {
return m_haveRSSI;
return m_rssi != -9999;
} // haveRSSI
@@ -260,7 +561,7 @@ bool NimBLEAdvertisedDevice::haveRSSI() {
* @return True if there is a service data value present.
*/
bool NimBLEAdvertisedDevice::haveServiceData() {
return m_haveServiceData;
return getServiceDataCount() > 0;
} // haveServiceData
@@ -269,7 +570,7 @@ bool NimBLEAdvertisedDevice::haveServiceData() {
* @return True if there is a service UUID value present.
*/
bool NimBLEAdvertisedDevice::haveServiceUUID() {
return m_haveServiceUUID;
return getServiceUUIDCount() > 0;
} // haveServiceUUID
@@ -278,143 +579,71 @@ bool NimBLEAdvertisedDevice::haveServiceUUID() {
* @return True if there is a transmission power value present.
*/
bool NimBLEAdvertisedDevice::haveTXPower() {
return m_haveTXPower;
return findAdvField(BLE_HS_ADV_TYPE_TX_PWR_LVL) > 0;
} // haveTXPower
/**
* @brief Parse the advertising pay load.
*
* The pay load is a buffer of bytes that is either 31 bytes long or terminated by
* a 0 length value. Each entry in the buffer has the format:
* [length][type][data...]
*
* The length does not include itself but does include everything after it until the next record. A record
* with a length value of 0 indicates a terminator.
*
* https://www.bluetooth.com/specifications/assigned-numbers/generic-access-profile
*/
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;
uint8_t NimBLEAdvertisedDevice::findAdvField(uint8_t type, uint8_t index, uint8_t *data_loc) {
ble_hs_adv_field *field = nullptr;
uint8_t data = 0;
uint8_t length = m_payload.size();
uint8_t count = 0;
if(length < 2) {
return count;
}
m_payload = payload;
m_payloadLength = length;
while (length > 1) {
field = (ble_hs_adv_field*)&m_payload[data];
#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 (field->length >= length) {
return count;
}
}
if (fields.uuids32 != NULL) {
for (int i = 0; i < fields.num_uuids32; i++) {
setServiceUUID(NimBLEUUID(fields.uuids32[i].value));
}
}
if (field->type == type) {
switch(type) {
case BLE_HS_ADV_TYPE_INCOMP_UUIDS16:
case BLE_HS_ADV_TYPE_COMP_UUIDS16:
count += field->length / 2;
break;
if (fields.uuids128 != NULL) {
for (int i = 0; i < fields.num_uuids128; i++) {
setServiceUUID(NimBLEUUID(&fields.uuids128[i]));
}
}
case BLE_HS_ADV_TYPE_INCOMP_UUIDS32:
case BLE_HS_ADV_TYPE_COMP_UUIDS32:
count += field->length / 4;
break;
if (fields.name != NULL) {
setName(std::string(reinterpret_cast<char*>(fields.name), fields.name_len));
}
case BLE_HS_ADV_TYPE_INCOMP_UUIDS128:
case BLE_HS_ADV_TYPE_COMP_UUIDS128:
count += field->length / 16;
break;
if (fields.tx_pwr_lvl_is_present) {
setTXPower(fields.tx_pwr_lvl);
}
case BLE_HS_ADV_TYPE_PUBLIC_TGT_ADDR:
case BLE_HS_ADV_TYPE_RANDOM_TGT_ADDR:
count += field->length / 6;
break;
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;
if(field->length > length) {
break;
default:
count++;
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(data_loc != nullptr) {
if(index == 0 || count >= index) {
break;
}
}
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;
}
length -= 1 + field->length;
data += 1 + field->length;
}
if (fields.appearance_is_present) {
setAppearance(fields.appearance);
if(data_loc != nullptr && field != nullptr) {
*data_loc = data;
}
if (fields.mfg_data != NULL) {
setManufacturerData(std::string(reinterpret_cast<char*>(fields.mfg_data), fields.mfg_data_len));
}
/* 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++) {
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) {
NIMBLE_LOGD(LOG_TAG, " slave_itvl_range=");
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.uri != NULL) {
NIMBLE_LOGD(LOG_TAG, " uri=");
print_bytes(fields.uri, fields.uri_len);
NIMBLE_LOGD(LOG_TAG, "\n");
}
*/
} //parseAdvertisement
return count;
}
/**
@@ -428,106 +657,22 @@ void NimBLEAdvertisedDevice::setAddress(NimBLEAddress address) {
/**
* @brief Set the adFlag for this device.
* @param [in] The discovered adFlag.
* @param [in] advType The advertisement flag data from the advertisement.
*/
void NimBLEAdvertisedDevice::setAdvType(uint8_t advType) {
m_advType = advType;
} // setAdvType
/**
* @brief Set the appearance for this device.
* @param [in] The discovered appearance.
*/
void NimBLEAdvertisedDevice::setAppearance(uint16_t appearance) {
m_appearance = appearance;
m_haveAppearance = true;
} // setAppearance
/**
* @brief Set the manufacturer data for this device.
* @param [in] The discovered manufacturer data.
*/
void NimBLEAdvertisedDevice::setManufacturerData(std::string manufacturerData) {
m_manufacturerData = manufacturerData;
m_haveManufacturerData = true;
} // setManufacturerData
/**
* @brief Set the name for this device.
* @param [in] name The discovered name.
*/
void NimBLEAdvertisedDevice::setName(std::string name) {
m_name = name;
m_haveName = true;
} // setName
/**
* @brief Set the RSSI for this device.
* @param [in] rssi The discovered RSSI.
* @param [in] rssi The RSSI of the discovered device.
*/
void NimBLEAdvertisedDevice::setRSSI(int rssi) {
m_rssi = rssi;
m_haveRSSI = true;
m_rssi = rssi;
} // setRSSI
/**
* @brief Set the Service UUID for this device.
* @param [in] serviceUUID The discovered serviceUUID
*/
void NimBLEAdvertisedDevice::setServiceUUID(const char* serviceUUID) {
return setServiceUUID(NimBLEUUID(serviceUUID));
} // setServiceUUID
/**
* @brief Set the Service UUID for this device.
* @param [in] serviceUUID The discovered serviceUUID
*/
void NimBLEAdvertisedDevice::setServiceUUID(NimBLEUUID serviceUUID) {
// Don't add duplicates
for (int i = 0; i < m_serviceUUIDs.size(); i++) {
if (m_serviceUUIDs[i] == serviceUUID) {
return;
}
}
m_serviceUUIDs.push_back(serviceUUID);
m_haveServiceUUID = true;
} // setServiceUUID
/**
* @brief Set the ServiceData value.
* @param [in] uuid The UUID that the service data belongs to.
* @param [in] data The service data.
*/
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 power level for this device.
* @param [in] txPower The discovered power level.
*/
void NimBLEAdvertisedDevice::setTXPower(int8_t txPower) {
m_txPower = txPower;
m_haveTXPower = true;
} // setTXPower
/**
* @brief Create a string representation of this device.
* @return A string representation of this device.
@@ -579,10 +724,35 @@ std::string NimBLEAdvertisedDevice::toString() {
* @return The advertisement payload.
*/
uint8_t* NimBLEAdvertisedDevice::getPayload() {
return m_payload;
return &m_payload[0];
} // getPayload
/**
* @brief Stores the payload of the advertised device in a vector.
* @param [in] payload The advertisement payload.
* @param [in] length The length of the payload in bytes.
* @param [in] append Indicates if the the data should be appended (scan response).
*/
void NimBLEAdvertisedDevice::setPayload(uint8_t *payload, uint8_t length, bool append) {
if(!append) {
m_advLength = length;
m_payload.assign(payload, payload + length);
} else {
m_payload.insert(m_payload.end(), payload, payload + length);
}
}
/**
* @brief Get the length of the advertisement data in the payload.
* @return The number of bytes in the payload that is from the advertisment.
*/
uint8_t NimBLEAdvertisedDevice::getAdvLength() {
return m_advLength;
}
/**
* @brief Get the advertised device address type.
* @return The device address type:
@@ -610,7 +780,7 @@ time_t NimBLEAdvertisedDevice::getTimestamp() {
* @return The size of the payload in bytes.
*/
size_t NimBLEAdvertisedDevice::getPayloadLength() {
return m_payloadLength;
return m_payload.size();
} // getPayloadLength

View File

@@ -44,7 +44,11 @@ public:
NimBLEAddress getAddress();
uint8_t getAdvType();
uint16_t getAppearance();
uint16_t getAdvInterval();
uint16_t getMinInterval();
uint16_t getMaxInterval();
std::string getManufacturerData();
std::string getURI();
/**
* @brief A template to convert the service data to <type\>.
@@ -67,7 +71,7 @@ public:
NimBLEScan* getScan();
size_t getServiceDataCount();
std::string getServiceData(uint8_t index = 0);
std::string getServiceData(const NimBLEUUID &uuid) const;
std::string getServiceData(const NimBLEUUID &uuid);
/**
* @brief A template to convert the service data to <tt><type\></tt>.
@@ -106,12 +110,15 @@ public:
NimBLEUUID getServiceDataUUID(uint8_t index = 0);
NimBLEUUID getServiceUUID(uint8_t index = 0);
size_t getServiceUUIDCount();
NimBLEAddress getTargetAddress(uint8_t index = 0);
size_t getTargetAddressCount();
int8_t getTXPower();
uint8_t* getPayload();
uint8_t getAdvLength();
size_t getPayloadLength();
uint8_t getAddressType();
time_t getTimestamp();
bool isAdvertisingService(const NimBLEUUID &uuid) const;
bool isAdvertisingService(const NimBLEUUID &uuid);
bool haveAppearance();
bool haveManufacturerData();
bool haveName();
@@ -119,46 +126,30 @@ public:
bool haveServiceData();
bool haveServiceUUID();
bool haveTXPower();
bool haveConnParams();
bool haveAdvInterval();
bool haveTargetAddress();
bool haveURI();
std::string toString();
private:
friend class NimBLEScan;
void parseAdvertisement(uint8_t* payload, uint8_t length);
void setAddress(NimBLEAddress address);
void setAdvType(uint8_t advType);
void setAppearance(uint16_t appearance);
void setManufacturerData(std::string manufacturerData);
void setName(std::string name);
void setRSSI(int rssi);
void setServiceData(NimBLEUUID serviceUUID, std::string data);
void setServiceUUID(const char* serviceUUID);
void setServiceUUID(NimBLEUUID serviceUUID);
void setTXPower(int8_t txPower);
bool m_haveAppearance;
bool m_haveManufacturerData;
bool m_haveName;
bool m_haveRSSI;
bool m_haveServiceData;
bool m_haveServiceUUID;
bool m_haveTXPower;
void setAddress(NimBLEAddress address);
void setAdvType(uint8_t advType);
void setPayload(uint8_t *payload, uint8_t length, bool append);
void setRSSI(int rssi);
uint8_t findAdvField(uint8_t type, uint8_t index = 0, uint8_t *data_loc = nullptr);
uint8_t findServiceData(uint8_t index, uint8_t* bytes);
NimBLEAddress m_address = NimBLEAddress("");
uint8_t m_advType;
uint16_t m_appearance;
std::string m_manufacturerData;
std::string m_name;
int m_rssi;
int8_t m_txPower;
uint8_t* m_payload;
size_t m_payloadLength;
time_t m_timestamp;
bool m_callbackSent;
uint8_t m_advLength;
std::vector<NimBLEUUID> m_serviceUUIDs;
std::vector<std::pair<NimBLEUUID, std::string>>m_serviceDataVec;
std::vector<uint8_t> m_payload;
};
/**

View File

@@ -33,33 +33,42 @@ static const char* LOG_TAG = "NimBLEAdvertising";
* @brief Construct a default advertising object.
*/
NimBLEAdvertising::NimBLEAdvertising() {
reset();
} // NimBLEAdvertising
/**
* @brief Stops the current advertising and resets the advertising data to the default values.
*/
void NimBLEAdvertising::reset() {
if(NimBLEDevice::getInitialized() && isAdvertising()) {
stop();
}
memset(&m_advData, 0, sizeof m_advData);
memset(&m_scanData, 0, sizeof m_scanData);
memset(&m_advParams, 0, sizeof m_advParams);
memset(&m_slaveItvl, 0, sizeof m_slaveItvl);
const char *name = ble_svc_gap_device_name();
m_advData.name = (uint8_t *)name;
m_advData.name_len = strlen(name);
m_advData.name_is_complete = 1;
m_scanData.tx_pwr_lvl_is_present = 1;
m_scanData.tx_pwr_lvl = NimBLEDevice::getPower();
m_advData.tx_pwr_lvl = NimBLEDevice::getPower();
m_advData.flags = (BLE_HS_ADV_F_DISC_GEN | BLE_HS_ADV_F_BREDR_UNSUP);
m_advData.appearance = 0;
m_advData.appearance_is_present = 0;
m_advData.mfg_data_len = 0;
m_advData.mfg_data = nullptr;
#if !defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
m_advParams.conn_mode = BLE_GAP_CONN_MODE_NON;
#else
m_advParams.conn_mode = BLE_GAP_CONN_MODE_UND;
#endif
m_advParams.disc_mode = BLE_GAP_DISC_MODE_GEN;
m_advParams.itvl_min = 0;
m_advParams.itvl_max = 0;
m_customAdvData = false;
m_customScanResponseData = false;
m_scanResp = true;
m_advDataSet = false;
} // NimBLEAdvertising
// Set this to non-zero to prevent auto start if host reset before started by app.
m_duration = BLE_HS_FOREVER;
} // reset
/**
@@ -78,6 +87,7 @@ void NimBLEAdvertising::addServiceUUID(const NimBLEUUID &serviceUUID) {
*/
void NimBLEAdvertising::addServiceUUID(const char* serviceUUID) {
addServiceUUID(NimBLEUUID(serviceUUID));
m_advDataSet = false;
} // addServiceUUID
@@ -86,7 +96,6 @@ void NimBLEAdvertising::addServiceUUID(const char* serviceUUID) {
* @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);
@@ -106,17 +115,101 @@ void NimBLEAdvertising::removeServiceUUID(const NimBLEUUID &serviceUUID) {
void NimBLEAdvertising::setAppearance(uint16_t appearance) {
m_advData.appearance = appearance;
m_advData.appearance_is_present = 1;
m_advDataSet = false;
} // setAppearance
/**
* @brief Add the transmission power level to the advertisement packet.
*/
void NimBLEAdvertising::addTxPower() {
m_advData.tx_pwr_lvl_is_present = 1;
m_advDataSet = false;
} // addTxPower
/**
* @brief Set the advertised name of the device.
* @param [in] name The name to advertise.
*/
void NimBLEAdvertising::setName(const std::string &name) {
m_name.assign(name.begin(), name.end());
m_advData.name = &m_name[0];
m_advData.name_len = m_name.size();
m_advDataSet = false;
} // setName
/**
* @brief Set the advertised manufacturer data.
* @param [in] data The data to advertise.
*/
void NimBLEAdvertising::setManufacturerData(const std::string &data) {
m_mfgData.assign(data.begin(), data.end());
m_advData.mfg_data = &m_mfgData[0];
m_advData.mfg_data_len = m_mfgData.size();
m_advDataSet = false;
} // setManufacturerData
/**
* @brief Set the advertised URI.
* @param [in] uri The URI to advertise.
*/
void NimBLEAdvertising::setURI(const std::string &uri) {
m_uri.assign(uri.begin(), uri.end());
m_advData.uri = &m_uri[0];
m_advData.uri_len = m_uri.size();
m_advDataSet = false;
} // setURI
/**
* @brief Set the service data advertised for the UUID.
* @param [in] uuid The UUID the service data belongs to.
* @param [in] data The data to advertise.
* @note If data length is 0 the service data will not be advertised.
*/
void NimBLEAdvertising::setServiceData(const NimBLEUUID &uuid, const std::string &data) {
switch (uuid.bitSize()) {
case 16: {
m_svcData16.assign((uint8_t*)&uuid.getNative()->u16.value, (uint8_t*)&uuid.getNative()->u16.value + 2);
m_svcData16.insert(m_svcData16.end(), data.begin(), data.end());
m_advData.svc_data_uuid16 = (uint8_t*)&m_svcData16[0];
m_advData.svc_data_uuid16_len = (data.length() > 0) ? m_svcData16.size() : 0;
break;
}
case 32: {
m_svcData32.assign((uint8_t*)&uuid.getNative()->u32.value, (uint8_t*)&uuid.getNative()->u32.value + 4);
m_svcData32.insert(m_svcData32.end(), data.begin(), data.end());
m_advData.svc_data_uuid32 = (uint8_t*)&m_svcData32[0];
m_advData.svc_data_uuid32_len = (data.length() > 0) ? m_svcData32.size() : 0;
break;
}
case 128: {
m_svcData128.assign(uuid.getNative()->u128.value, uuid.getNative()->u128.value + 16);
m_svcData128.insert(m_svcData128.end(), data.begin(), data.end());
m_advData.svc_data_uuid128 = (uint8_t*)&m_svcData128[0];
m_advData.svc_data_uuid128_len = (data.length() > 0) ? m_svcData128.size() : 0;
break;
}
default:
return;
}
m_advDataSet = false;
} // setServiceData
/**
* @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
* * BLE_GAP_CONN_MODE_NON (0) - not connectable advertising
* * BLE_GAP_CONN_MODE_DIR (1) - directed connectable advertising
* * BLE_GAP_CONN_MODE_UND (2) - undirected connectable advertising
*/
void NimBLEAdvertising::setAdvertisementType(uint8_t adv_type){
m_advParams.conn_mode = adv_type;
@@ -141,12 +234,75 @@ void NimBLEAdvertising::setMaxInterval(uint16_t maxinterval) {
} // setMaxInterval
/**
* @brief Set the advertised min connection interval preferred by this device.
* @param [in] mininterval the max interval value. Range = 0x0006 to 0x0C80.
* @details Values not within the range will cancel advertising of this data.\n
* Consumes 6 bytes of advertising space (combined with max interval).
*/
void NimBLEAdvertising::setMinPreferred(uint16_t mininterval) {
// invalid paramters, set the slave interval to null
if(mininterval < 0x0006 || mininterval > 0x0C80) {
m_advData.slave_itvl_range = nullptr;
return;
}
if(m_advData.slave_itvl_range == nullptr) {
m_advData.slave_itvl_range = m_slaveItvl;
}
m_slaveItvl[0] = mininterval;
m_slaveItvl[1] = mininterval >> 8;
uint16_t maxinterval = *(uint16_t*)(m_advData.slave_itvl_range+2);
// If mininterval is higher than the maxinterval make them the same
if(mininterval > maxinterval) {
m_slaveItvl[2] = m_slaveItvl[0];
m_slaveItvl[3] = m_slaveItvl[1];
}
m_advDataSet = false;
} // setMinPreferred
/**
* @brief Set the advertised max connection interval preferred by this device.
* @param [in] maxinterval the max interval value. Range = 0x0006 to 0x0C80.
* @details Values not within the range will cancel advertising of this data.\n
* Consumes 6 bytes of advertising space (combined with min interval).
*/
void NimBLEAdvertising::setMaxPreferred(uint16_t maxinterval) {
// invalid paramters, set the slave interval to null
if(maxinterval < 0x0006 || maxinterval > 0x0C80) {
m_advData.slave_itvl_range = nullptr;
return;
}
if(m_advData.slave_itvl_range == nullptr) {
m_advData.slave_itvl_range = m_slaveItvl;
}
m_slaveItvl[2] = maxinterval;
m_slaveItvl[3] = maxinterval >> 8;
uint16_t mininterval = *(uint16_t*)(m_advData.slave_itvl_range);
// If mininterval is higher than the maxinterval make them the same
if(mininterval > maxinterval) {
m_slaveItvl[0] = m_slaveItvl[2];
m_slaveItvl[1] = m_slaveItvl[3];
}
m_advDataSet = false;
} // setMaxPreferred
/**
* @brief Set if scan response is available.
* @param [in] set true = scan response available.
*/
void NimBLEAdvertising::setScanResponse(bool set) {
m_scanResp = set;
m_advDataSet = false;
} // setScanResponse
@@ -156,7 +312,8 @@ void NimBLEAdvertising::setScanResponse(bool set) {
* @param [in] connectWhitelistOnly If true, only allow connections from those on the white list.
*/
void NimBLEAdvertising::setScanFilter(bool scanRequestWhitelistOnly, bool connectWhitelistOnly) {
NIMBLE_LOGD(LOG_TAG, ">> setScanFilter: scanRequestWhitelistOnly: %d, connectWhitelistOnly: %d", scanRequestWhitelistOnly, connectWhitelistOnly);
NIMBLE_LOGD(LOG_TAG, ">> setScanFilter: scanRequestWhitelistOnly: %d, connectWhitelistOnly: %d",
scanRequestWhitelistOnly, connectWhitelistOnly);
if (!scanRequestWhitelistOnly && !connectWhitelistOnly) {
m_advParams.filter_policy = BLE_HCI_ADV_FILT_NONE;
NIMBLE_LOGD(LOG_TAG, "<< setScanFilter");
@@ -194,7 +351,8 @@ void NimBLEAdvertising::setAdvertisementData(NimBLEAdvertisementData& advertisem
(uint8_t*)advertisementData.getPayload().data(),
advertisementData.getPayload().length());
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "ble_gap_adv_set_data: %d %s", rc, NimBLEUtils::returnCodeToString(rc));
NIMBLE_LOGE(LOG_TAG, "ble_gap_adv_set_data: %d %s",
rc, NimBLEUtils::returnCodeToString(rc));
}
m_customAdvData = true; // Set the flag that indicates we are using custom advertising data.
NIMBLE_LOGD(LOG_TAG, "<< setAdvertisementData");
@@ -213,7 +371,8 @@ void NimBLEAdvertising::setScanResponseData(NimBLEAdvertisementData& advertiseme
(uint8_t*)advertisementData.getPayload().data(),
advertisementData.getPayload().length());
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "ble_gap_adv_rsp_set_data: %d %s", rc, NimBLEUtils::returnCodeToString(rc));
NIMBLE_LOGE(LOG_TAG, "ble_gap_adv_rsp_set_data: %d %s",
rc, NimBLEUtils::returnCodeToString(rc));
}
m_customScanResponseData = true; // Set the flag that indicates we are using custom scan response data.
NIMBLE_LOGD(LOG_TAG, "<< setScanResponseData");
@@ -225,13 +384,14 @@ void NimBLEAdvertising::setScanResponseData(NimBLEAdvertisementData& advertiseme
* @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(uint32_t duration, void (*advCompleteCB)(NimBLEAdvertising *pAdv)) {
NIMBLE_LOGD(LOG_TAG, ">> Advertising start: customAdvData: %d, customScanResponseData: %d", m_customAdvData, m_customScanResponseData);
bool 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.
if(!NimBLEDevice::m_synced) {
NIMBLE_LOGE(LOG_TAG, "Host reset, wait for sync.");
return;
return false;
}
#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
@@ -240,17 +400,21 @@ void NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdv
if(!pServer->m_gattsStarted){
pServer->start();
} else if(pServer->getConnectedCount() >= NIMBLE_MAX_CONNECTIONS) {
NIMBLE_LOGW(LOG_TAG, "Max connections reached - not advertising");
return;
NIMBLE_LOGE(LOG_TAG, "Max connections reached - not advertising");
return false;
}
}
#endif
// If already advertising just return
if(ble_gap_adv_active()) {
return;
NIMBLE_LOGW(LOG_TAG, "Advertising already active");
return false;
}
// Save the duration incase of host reset so we can restart with the same params
m_duration = duration;
if(duration == 0){
duration = BLE_HS_FOREVER;
}
@@ -260,16 +424,48 @@ void NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdv
m_advCompCB = advCompleteCB;
m_advParams.disc_mode = BLE_GAP_DISC_MODE_GEN;
m_advData.flags = (BLE_HS_ADV_F_DISC_GEN | BLE_HS_ADV_F_BREDR_UNSUP);
if(m_advParams.conn_mode == BLE_GAP_CONN_MODE_NON) {
if(!m_scanResp) {
m_advParams.disc_mode = BLE_GAP_DISC_MODE_NON;
m_advData.flags = BLE_HS_ADV_F_BREDR_UNSUP;
}
}
int rc = 0;
if (!m_customAdvData && !m_advDataSet) {
//start with 3 bytes for the flags data
uint8_t payloadLen = 3;
uint8_t payloadLen = (2 + 1);
if(m_advData.mfg_data_len > 0)
payloadLen += (2 + m_advData.mfg_data_len);
if(m_advData.svc_data_uuid16_len > 0)
payloadLen += (2 + m_advData.svc_data_uuid16_len);
if(m_advData.svc_data_uuid32_len > 0)
payloadLen += (2 + m_advData.svc_data_uuid32_len);
if(m_advData.svc_data_uuid128_len > 0)
payloadLen += (2 + m_advData.svc_data_uuid128_len);
if(m_advData.uri_len > 0)
payloadLen += (2 + m_advData.uri_len);
if(m_advData.appearance_is_present)
payloadLen += (2 + BLE_HS_ADV_APPEARANCE_LEN);
if(m_advData.tx_pwr_lvl_is_present)
payloadLen += (2 + BLE_HS_ADV_TX_PWR_LVL_LEN);
if(m_advData.slave_itvl_range != nullptr)
payloadLen += (2 + BLE_HS_ADV_SLAVE_ITVL_RANGE_LEN);
for(auto &it : m_serviceUUIDs) {
if(it.getNative()->u.type == BLE_UUID_TYPE_16) {
int add = (m_advData.num_uuids16 > 0) ? 2 : 4;
if((payloadLen + add) > 31){
if((payloadLen + add) > BLE_HS_ADV_MAX_SZ){
m_advData.uuids16_is_complete = 0;
continue;
}
@@ -278,7 +474,7 @@ void NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdv
if(nullptr == (m_advData.uuids16 = (ble_uuid16_t*)realloc(m_advData.uuids16,
(m_advData.num_uuids16 + 1) * sizeof(ble_uuid16_t))))
{
NIMBLE_LOGE(LOG_TAG, "Error, no mem");
NIMBLE_LOGC(LOG_TAG, "Error, no mem");
abort();
}
memcpy(&m_advData.uuids16[m_advData.num_uuids16].value,
@@ -290,7 +486,7 @@ void NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdv
}
if(it.getNative()->u.type == BLE_UUID_TYPE_32) {
int add = (m_advData.num_uuids32 > 0) ? 4 : 6;
if((payloadLen + add) > 31){
if((payloadLen + add) > BLE_HS_ADV_MAX_SZ){
m_advData.uuids32_is_complete = 0;
continue;
}
@@ -299,7 +495,7 @@ void NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdv
if(nullptr == (m_advData.uuids32 = (ble_uuid32_t*)realloc(m_advData.uuids32,
(m_advData.num_uuids32 + 1) * sizeof(ble_uuid32_t))))
{
NIMBLE_LOGE(LOG_TAG, "Error, no mem");
NIMBLE_LOGC(LOG_TAG, "Error, no mem");
abort();
}
memcpy(&m_advData.uuids32[m_advData.num_uuids32].value,
@@ -311,7 +507,7 @@ void NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdv
}
if(it.getNative()->u.type == BLE_UUID_TYPE_128){
int add = (m_advData.num_uuids128 > 0) ? 16 : 18;
if((payloadLen + add) > 31){
if((payloadLen + add) > BLE_HS_ADV_MAX_SZ){
m_advData.uuids128_is_complete = 0;
continue;
}
@@ -320,7 +516,7 @@ void NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdv
if(nullptr == (m_advData.uuids128 = (ble_uuid128_t*)realloc(m_advData.uuids128,
(m_advData.num_uuids128 + 1) * sizeof(ble_uuid128_t))))
{
NIMBLE_LOGE(LOG_TAG, "Error, no mem");
NIMBLE_LOGC(LOG_TAG, "Error, no mem");
abort();
}
memcpy(&m_advData.uuids128[m_advData.num_uuids128].value,
@@ -333,54 +529,73 @@ void NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdv
}
// check if there is room for the name, if not put it in scan data
if((payloadLen + m_advData.name_len) > 29) {
if(m_scanResp){
if((payloadLen + (2 + m_advData.name_len)) > BLE_HS_ADV_MAX_SZ) {
if(m_scanResp && !m_customScanResponseData){
m_scanData.name = m_advData.name;
m_scanData.name_len = m_advData.name_len;
m_scanData.name_is_complete = m_advData.name_is_complete;
if(m_scanData.name_len > BLE_HS_ADV_MAX_SZ - 2) {
m_scanData.name_len = BLE_HS_ADV_MAX_SZ - 2;
m_scanData.name_is_complete = 0;
} else {
m_scanData.name_is_complete = 1;
}
m_advData.name = nullptr;
m_advData.name_len = 0;
m_advData.name_is_complete = 0;
} else {
if(m_advData.tx_pwr_lvl_is_present) {
m_advData.tx_pwr_lvl_is_present = 0;
payloadLen -= (2 + 1);
}
// if not using scan response just cut the name down
// leaving 2 bytes for the data specifier.
m_advData.name_len = (29 - payloadLen);
}
m_advData.name_is_complete = 0;
}
if(m_advData.name_len > 0) {
payloadLen += (m_advData.name_len + 2);
}
if(m_scanResp) {
// name length + type byte + length byte + tx power type + length + data
if((m_scanData.name_len + 5) > 31) {
// prioritize name data over tx power
m_scanData.tx_pwr_lvl_is_present = 0;
m_scanData.tx_pwr_lvl = 0;
// limit name to 29 to leave room for the data specifiers
if(m_scanData.name_len > 29) {
m_scanData.name_len = 29;
m_scanData.name_is_complete = false;
if(m_advData.name_len > (BLE_HS_ADV_MAX_SZ - payloadLen - 2)) {
m_advData.name_len = (BLE_HS_ADV_MAX_SZ - payloadLen - 2);
m_advData.name_is_complete = 0;
}
}
rc = ble_gap_adv_rsp_set_fields(&m_scanData);
if (rc != 0) {
NIMBLE_LOGC(LOG_TAG, "error setting scan response data; rc=%d, %s", rc, NimBLEUtils::returnCodeToString(rc));
abort();
}
// if not using scan response and there is room,
// put the tx power data into the advertisment
} else if (payloadLen < 29) {
m_advData.tx_pwr_lvl_is_present = 1;
m_advData.tx_pwr_lvl = NimBLEDevice::getPower();
}
rc = ble_gap_adv_set_fields(&m_advData);
if (rc != 0) {
NIMBLE_LOGC(LOG_TAG, "error setting advertisement data; rc=%d, %s", rc, NimBLEUtils::returnCodeToString(rc));
abort();
if(m_scanResp && !m_customScanResponseData) {
rc = ble_gap_adv_rsp_set_fields(&m_scanData);
switch(rc) {
case 0:
break;
case BLE_HS_EBUSY:
NIMBLE_LOGE(LOG_TAG, "Already advertising");
break;
case BLE_HS_EMSGSIZE:
NIMBLE_LOGE(LOG_TAG, "Scan data too long");
break;
default:
NIMBLE_LOGE(LOG_TAG, "Error setting scan response data; rc=%d, %s",
rc, NimBLEUtils::returnCodeToString(rc));
break;
}
}
if(rc == 0) {
rc = ble_gap_adv_set_fields(&m_advData);
switch(rc) {
case 0:
break;
case BLE_HS_EBUSY:
NIMBLE_LOGE(LOG_TAG, "Already advertising");
break;
case BLE_HS_EMSGSIZE:
NIMBLE_LOGE(LOG_TAG, "Advertisement data too long");
break;
default:
NIMBLE_LOGE(LOG_TAG, "Error setting advertisement data; rc=%d, %s",
rc, NimBLEUtils::returnCodeToString(rc));
break;
}
}
if(m_advData.num_uuids128 > 0) {
@@ -401,24 +616,54 @@ void NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdv
m_advData.num_uuids16 = 0;
}
if(rc !=0) {
return false;
}
m_advDataSet = true;
}
#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
rc = ble_gap_adv_start(0, NULL, duration,
rc = ble_gap_adv_start(NimBLEDevice::m_own_addr_type, NULL, duration,
&m_advParams,
(pServer != nullptr) ? NimBLEServer::handleGapEvent : NimBLEAdvertising::handleGapEvent,
(pServer != nullptr) ? NimBLEServer::handleGapEvent :
NimBLEAdvertising::handleGapEvent,
(pServer != nullptr) ? (void*)pServer : (void*)this);
#else
rc = ble_gap_adv_start(0, NULL, duration,
rc = ble_gap_adv_start(NimBLEDevice::m_own_addr_type, 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));
abort();
switch(rc) {
case 0:
break;
case BLE_HS_EINVAL:
NIMBLE_LOGE(LOG_TAG, "Unable to advertise - Duration too long");
break;
case BLE_HS_EPREEMPTED:
NIMBLE_LOGE(LOG_TAG, "Unable to advertise - busy");
break;
case BLE_HS_ETIMEOUT_HCI:
case BLE_HS_EOS:
case BLE_HS_ECONTROLLER:
case BLE_HS_ENOTSYNCED:
NIMBLE_LOGE(LOG_TAG, "Unable to advertise - Host Reset");
break;
default:
NIMBLE_LOGE(LOG_TAG, "Error enabling advertising; rc=%d, %s",
rc, NimBLEUtils::returnCodeToString(rc));
break;
}
if(rc != 0) {
return false;
}
NIMBLE_LOGD(LOG_TAG, "<< Advertising start");
return true;
} // start
@@ -427,9 +672,11 @@ void NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdv
*/
void NimBLEAdvertising::stop() {
NIMBLE_LOGD(LOG_TAG, ">> stop");
int rc = ble_gap_adv_stop();
if (rc != 0 && rc != BLE_HS_EALREADY) {
NIMBLE_LOGE(LOG_TAG, "ble_gap_adv_stop rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc));
NIMBLE_LOGE(LOG_TAG, "ble_gap_adv_stop rc=%d %s",
rc, NimBLEUtils::returnCodeToString(rc));
return;
}
@@ -444,7 +691,7 @@ void NimBLEAdvertising::advCompleteCB() {
if(m_advCompCB != nullptr) {
m_advCompCB(this);
}
}
} // advCompleteCB
/**
@@ -453,16 +700,25 @@ void NimBLEAdvertising::advCompleteCB() {
*/
bool NimBLEAdvertising::isAdvertising() {
return ble_gap_adv_active();
}
} // isAdvertising
/*
* Host reset seems to clear advertising data,
* we need clear the flag so it reloads it.
*/
void NimBLEAdvertising::onHostReset() {
void NimBLEAdvertising::onHostSync() {
NIMBLE_LOGD(LOG_TAG, "Host re-synced");
m_advDataSet = false;
}
// If we were advertising forever, restart it now
if(m_duration == 0) {
start(m_duration, m_advCompCB);
} else {
// Otherwise we should tell the app that advertising stopped.
advCompleteCB();
}
} // onHostSync
/**
@@ -475,6 +731,19 @@ int NimBLEAdvertising::handleGapEvent(struct ble_gap_event *event, void *arg) {
NimBLEAdvertising *pAdv = (NimBLEAdvertising*)arg;
if(event->type == BLE_GAP_EVENT_ADV_COMPLETE) {
switch(event->adv_complete.reason) {
// Don't call the callback if host reset, we want to
// preserve the active flag until re-sync to restart advertising.
case BLE_HS_ETIMEOUT_HCI:
case BLE_HS_EOS:
case BLE_HS_ECONTROLLER:
case BLE_HS_ENOTSYNCED:
NIMBLE_LOGC(LOG_TAG, "host reset, rc=%d", event->adv_complete.reason);
NimBLEDevice::onReset(event->adv_complete.reason);
return 0;
default:
break;
}
pAdv->advCompleteCB();
}
return 0;
@@ -487,6 +756,7 @@ int NimBLEAdvertising::handleGapEvent(struct ble_gap_event *event, void *arg) {
*/
void NimBLEAdvertisementData::addData(const std::string &data) {
if ((m_payload.length() + data.length()) > BLE_HS_ADV_MAX_SZ) {
NIMBLE_LOGE(LOG_TAG, "Advertisement data length exceded");
return;
}
m_payload.append(data);
@@ -498,11 +768,8 @@ void NimBLEAdvertisementData::addData(const std::string &data) {
* @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);
void NimBLEAdvertisementData::addData(char * data, size_t length) {
addData(std::string(data, length));
} // addData
@@ -521,43 +788,6 @@ void NimBLEAdvertisementData::setAppearance(uint16_t appearance) {
} // setAppearance
/**
* @brief Set the complete services to advertise.
* @param [in] uuid The UUID of the service.
*/
void NimBLEAdvertisementData::setCompleteServices(const NimBLEUUID &uuid) {
char cdata[2];
switch (uuid.bitSize()) {
case 16: {
// [Len] [0x02] [LL] [HH]
cdata[0] = 3;
cdata[1] = BLE_HS_ADV_TYPE_COMP_UUIDS16; // 0x03
addData(std::string(cdata, 2) + std::string((char*) &uuid.getNative()->u16.value, 2));
break;
}
case 32: {
// [Len] [0x04] [LL] [LL] [HH] [HH]
cdata[0] = 5;
cdata[1] = BLE_HS_ADV_TYPE_COMP_UUIDS32; // 0x05
addData(std::string(cdata, 2) + std::string((char*) &uuid.getNative()->u32.value, 4));
break;
}
case 128: {
// [Len] [0x04] [0] [1] ... [15]
cdata[0] = 17;
cdata[1] = BLE_HS_ADV_TYPE_COMP_UUIDS128; // 0x07
addData(std::string(cdata, 2) + std::string((char*) uuid.getNative()->u128.value, 16));
break;
}
default:
return;
}
} // setCompleteServices
/**
* @brief Set the advertisement flags.
* @param [in] flag The flags to be set in the advertisement.
@@ -579,64 +809,141 @@ void NimBLEAdvertisementData::setFlags(uint8_t flag) {
* @param [in] data The manufacturer data to advertise.
*/
void NimBLEAdvertisementData::setManufacturerData(const std::string &data) {
NIMBLE_LOGD("NimBLEAdvertisementData", ">> setManufacturerData");
char cdata[2];
cdata[0] = data.length() + 1;
cdata[1] = BLE_HS_ADV_TYPE_MFG_DATA ; // 0xff
addData(std::string(cdata, 2) + data);
NIMBLE_LOGD("NimBLEAdvertisementData", "<< setManufacturerData");
} // setManufacturerData
/**
* @brief Set the URI to advertise.
* @param [in] uri The uri to advertise.
*/
void NimBLEAdvertisementData::setURI(const std::string &uri) {
char cdata[2];
cdata[0] = uri.length() + 1;
cdata[1] = BLE_HS_ADV_TYPE_URI;
addData(std::string(cdata, 2) + uri);
} // setURI
/**
* @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());
char cdata[2];
cdata[0] = name.length() + 1;
cdata[1] = BLE_HS_ADV_TYPE_COMP_NAME; // 0x09
addData(std::string(cdata, 2) + name);
NIMBLE_LOGD("NimBLEAdvertisementData", "<< setName");
} // setName
/**
* @brief Set the partial services to advertise.
* @param [in] uuid The single service to advertise.
* @brief Set a single service to advertise as a complete list of services.
* @param [in] uuid The service to advertise.
*/
void NimBLEAdvertisementData::setCompleteServices(const NimBLEUUID &uuid) {
setServices(true, uuid.bitSize(), {uuid});
} // setCompleteServices
/**
* @brief Set the complete list of 16 bit services to advertise.
* @param [in] v_uuid A vector of 16 bit UUID's to advertise.
*/
void NimBLEAdvertisementData::setCompleteServices16(const std::vector<NimBLEUUID>& v_uuid) {
setServices(true, 16, v_uuid);
} // setCompleteServices16
/**
* @brief Set the complete list of 32 bit services to advertise.
* @param [in] v_uuid A vector of 32 bit UUID's to advertise.
*/
void NimBLEAdvertisementData::setCompleteServices32(const std::vector<NimBLEUUID>& v_uuid) {
setServices(true, 32, v_uuid);
} // setCompleteServices32
/**
* @brief Set a single service to advertise as a partial list of services.
* @param [in] uuid The service to advertise.
*/
void NimBLEAdvertisementData::setPartialServices(const NimBLEUUID &uuid) {
setServices(false, uuid.bitSize(), {uuid});
} // setPartialServices
/**
* @brief Set the partial list of services to advertise.
* @param [in] v_uuid A vector of 16 bit UUID's to advertise.
*/
void NimBLEAdvertisementData::setPartialServices16(const std::vector<NimBLEUUID>& v_uuid) {
setServices(false, 16, v_uuid);
} // setPartialServices16
/**
* @brief Set the partial list of services to advertise.
* @param [in] v_uuid A vector of 32 bit UUID's to advertise.
*/
void NimBLEAdvertisementData::setPartialServices32(const std::vector<NimBLEUUID>& v_uuid) {
setServices(false, 32, v_uuid);
} // setPartialServices32
/**
* @brief Utility function to create the list of service UUID's from a vector.
* @param [in] complete If true the vector is the complete set of services.
* @param [in] size The bit size of the UUID's in the vector. (16, 32, or 128).
* @param [in] v_uuid The vector of service UUID's to advertise.
*/
void NimBLEAdvertisementData::setServices(const bool complete, const uint8_t size,
const std::vector<NimBLEUUID> &v_uuid)
{
char cdata[2];
switch (uuid.bitSize()) {
case 16: {
// [Len] [0x02] [LL] [HH]
cdata[0] = 3;
cdata[1] = BLE_HS_ADV_TYPE_INCOMP_UUIDS16; // 0x02
addData(std::string(cdata, 2) + std::string((char *) &uuid.getNative()->u16.value, 2));
cdata[0] = (size / 8) * v_uuid.size() + 1;
switch(size) {
case 16:
cdata[1] = complete ? BLE_HS_ADV_TYPE_COMP_UUIDS16 : BLE_HS_ADV_TYPE_INCOMP_UUIDS16;
break;
}
case 32: {
// [Len] [0x04] [LL] [LL] [HH] [HH]
cdata[0] = 5;
cdata[1] = BLE_HS_ADV_TYPE_INCOMP_UUIDS32; // 0x04
addData(std::string(cdata, 2) + std::string((char *) &uuid.getNative()->u32.value, 4));
case 32:
cdata[1] = complete ? BLE_HS_ADV_TYPE_COMP_UUIDS32 : BLE_HS_ADV_TYPE_INCOMP_UUIDS32;
break;
}
case 128: {
// [Len] [0x04] [0] [1] ... [15]
cdata[0] = 17;
cdata[1] = BLE_HS_ADV_TYPE_INCOMP_UUIDS128; // 0x06
addData(std::string(cdata, 2) + std::string((char *) &uuid.getNative()->u128.value, 16));
case 128:
cdata[1] = complete ? BLE_HS_ADV_TYPE_COMP_UUIDS128 : BLE_HS_ADV_TYPE_INCOMP_UUIDS128;
break;
}
default:
return;
}
} // setPartialServices
std::string uuids;
for(auto &it : v_uuid){
if(it.bitSize() != size) {
NIMBLE_LOGE(LOG_TAG, "Service UUID(%d) invalid", size);
return;
} else {
switch(size) {
case 16:
uuids += std::string((char*)&it.getNative()->u16.value, 2);
break;
case 32:
uuids += std::string((char*)&it.getNative()->u32.value, 4);
break;
case 128:
uuids += std::string((char*)&it.getNative()->u128.value, 16);
break;
default:
return;
}
}
}
addData(std::string(cdata, 2) + uuids);
} // setServices
/**
@@ -682,15 +989,42 @@ void NimBLEAdvertisementData::setServiceData(const NimBLEUUID &uuid, const std::
* @param [in] name The short name of the device.
*/
void NimBLEAdvertisementData::setShortName(const std::string &name) {
NIMBLE_LOGD("NimBLEAdvertisementData", ">> setShortName: %s", name.c_str());
char cdata[2];
cdata[0] = name.length() + 1;
cdata[1] = BLE_HS_ADV_TYPE_INCOMP_NAME; // 0x08
addData(std::string(cdata, 2) + name);
NIMBLE_LOGD("NimBLEAdvertisementData", "<< setShortName");
} // setShortName
/**
* @brief Adds Tx power level to the advertisement data.
*/
void NimBLEAdvertisementData::addTxPower() {
char cdata[3];
cdata[0] = BLE_HS_ADV_TX_PWR_LVL_LEN + 1;
cdata[1] = BLE_HS_ADV_TYPE_TX_PWR_LVL;
cdata[2] = NimBLEDevice::getPower();
addData(cdata, 3);
} // addTxPower
/**
* @brief Set the preferred connection interval parameters.
* @param [in] min The minimum interval desired.
* @param [in] max The maximum interval desired.
*/
void NimBLEAdvertisementData::setPreferredParams(uint16_t min, uint16_t max) {
char cdata[6];
cdata[0] = BLE_HS_ADV_SLAVE_ITVL_RANGE_LEN + 1;
cdata[1] = BLE_HS_ADV_TYPE_SLAVE_ITVL_RANGE;
cdata[2] = min;
cdata[3] = min >> 8;
cdata[4] = max;
cdata[5] = max >> 8;
addData(cdata, 6);
} // setPreferredParams
/**
* @brief Retrieve the payload that is to be advertised.
* @return The payload that is to be advertised.

View File

@@ -50,18 +50,27 @@ class NimBLEAdvertisementData {
public:
void setAppearance(uint16_t appearance);
void setCompleteServices(const NimBLEUUID &uuid);
void setCompleteServices16(const std::vector<NimBLEUUID> &v_uuid);
void setCompleteServices32(const std::vector<NimBLEUUID> &v_uuid);
void setFlags(uint8_t);
void setManufacturerData(const std::string &data);
void setURI(const std::string &uri);
void setName(const std::string &name);
void setPartialServices(const NimBLEUUID &uuid);
void setPartialServices16(const std::vector<NimBLEUUID> &v_uuid);
void setPartialServices32(const std::vector<NimBLEUUID> &v_uuid);
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);
void addTxPower();
void setPreferredParams(uint16_t min, uint16_t max);
std::string getPayload(); // Retrieve the current advert payload.
private:
friend class NimBLEAdvertising;
void setServices(const bool complete, const uint8_t size,
const std::vector<NimBLEUUID> &v_uuid);
std::string m_payload; // The payload of the advertisement.
}; // NimBLEAdvertisementData
@@ -77,9 +86,13 @@ public:
void addServiceUUID(const NimBLEUUID &serviceUUID);
void addServiceUUID(const char* serviceUUID);
void removeServiceUUID(const NimBLEUUID &serviceUUID);
void start(uint32_t duration = 0, void (*advCompleteCB)(NimBLEAdvertising *pAdv) = nullptr);
bool start(uint32_t duration = 0, void (*advCompleteCB)(NimBLEAdvertising *pAdv) = nullptr);
void stop();
void setAppearance(uint16_t appearance);
void setName(const std::string &name);
void setManufacturerData(const std::string &data);
void setURI(const std::string &uri);
void setServiceData(const NimBLEUUID &uuid, const std::string &data);
void setAdvertisementType(uint8_t adv_type);
void setMaxInterval(uint16_t maxinterval);
void setMinInterval(uint16_t mininterval);
@@ -87,13 +100,17 @@ public:
void setScanFilter(bool scanRequestWhitelistOnly, bool connectWhitelistOnly);
void setScanResponseData(NimBLEAdvertisementData& advertisementData);
void setScanResponse(bool);
void setMinPreferred(uint16_t);
void setMaxPreferred(uint16_t);
void addTxPower();
void reset();
void advCompleteCB();
bool isAdvertising();
private:
friend class NimBLEDevice;
void onHostReset();
void onHostSync();
static int handleGapEvent(struct ble_gap_event *event, void *arg);
ble_hs_adv_fields m_advData;
@@ -104,8 +121,15 @@ private:
bool m_customScanResponseData;
bool m_scanResp;
bool m_advDataSet;
void (*m_advCompCB)(NimBLEAdvertising *pAdv);
void (*m_advCompCB)(NimBLEAdvertising *pAdv);
uint8_t m_slaveItvl[4];
uint32_t m_duration;
std::vector<uint8_t> m_svcData16;
std::vector<uint8_t> m_svcData32;
std::vector<uint8_t> m_svcData128;
std::vector<uint8_t> m_name;
std::vector<uint8_t> m_mfgData;
std::vector<uint8_t> m_uri;
};
#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)

View File

@@ -101,9 +101,9 @@ NimBLEDescriptor* NimBLECharacteristic::createDescriptor(const NimBLEUUID &uuid,
/**
* @brief Return the BLE Descriptor for the given UUID if associated with this characteristic.
* @param [in] uuid The UUID of the descriptor that we wish to retrieve.
* @return pointer to the NimBLEDescriptor. If no such descriptor is associated with the characteristic, nullptr is returned.
* @brief Return the BLE Descriptor for the given UUID.
* @param [in] uuid The UUID of the descriptor.
* @return A pointer to the descriptor object or nullptr if not found.
*/
NimBLEDescriptor* NimBLECharacteristic::getDescriptorByUUID(const char* uuid) {
return getDescriptorByUUID(NimBLEUUID(uuid));
@@ -111,9 +111,9 @@ NimBLEDescriptor* NimBLECharacteristic::getDescriptorByUUID(const char* uuid) {
/**
* @brief Return the BLE Descriptor for the given UUID if associated with this characteristic.
* @param [in] uuid The UUID of the descriptor that we wish to retrieve.
* @return pointer to the NimBLEDescriptor. If no such descriptor is associated with the characteristic, nullptr is returned.
* @brief Return the BLE Descriptor for the given UUID.
* @param [in] uuid The UUID of the descriptor.
* @return A pointer to the descriptor object or nullptr if not found.
*/
NimBLEDescriptor* NimBLECharacteristic::getDescriptorByUUID(const NimBLEUUID &uuid) {
for (auto &it : m_dscVec) {
@@ -124,6 +124,20 @@ NimBLEDescriptor* NimBLECharacteristic::getDescriptorByUUID(const NimBLEUUID &uu
return nullptr;
} // getDescriptorByUUID
/**
* @brief Return the BLE Descriptor for the given handle.
* @param [in] handle The handle of the descriptor.
* @return A pointer to the descriptor object or nullptr if not found.
*/
NimBLEDescriptor *NimBLECharacteristic::getDescriptorByHandle(uint16_t handle) {
for (auto &it : m_dscVec) {
if (it->getHandle() == handle) {
return it;
}
}
return nullptr;
}
/**
* @brief Get the handle of the characteristic.
@@ -473,9 +487,10 @@ void NimBLECharacteristic::setValue(const uint8_t* data, size_t length) {
return;
}
time_t t = time(nullptr);
portENTER_CRITICAL(&m_valMux);
m_value = std::string((char*)data, length);
m_timestamp = time(nullptr);
m_timestamp = t;
portEXIT_CRITICAL(&m_valMux);
NIMBLE_LOGD(LOG_TAG, "<< setValue");

View File

@@ -59,6 +59,17 @@ class NimBLECharacteristicCallbacks;
*/
class NimBLECharacteristic {
public:
uint16_t getHandle();
NimBLEUUID getUUID();
std::string toString();
void setCallbacks(NimBLECharacteristicCallbacks* pCallbacks);
void indicate();
void notify(bool is_notification = true);
size_t getSubscribedCount();
NimBLEDescriptor* createDescriptor(const char* uuid,
uint32_t properties =
NIMBLE_PROPERTY::READ |
@@ -72,9 +83,10 @@ public:
NimBLEDescriptor* getDescriptorByUUID(const char* uuid);
NimBLEDescriptor* getDescriptorByUUID(const NimBLEUUID &uuid);
NimBLEUUID getUUID();
std::string getValue(time_t *timestamp = nullptr);
NimBLEDescriptor* getDescriptorByHandle(uint16_t handle);
std::string getValue(time_t *timestamp = nullptr);
size_t getDataLength();
/**
* @brief A template to convert the characteristic data to <type\>.
* @tparam T The type to convert the data to.
@@ -92,25 +104,20 @@ public:
return *((T *)pData);
}
size_t getDataLength();
void indicate();
void notify(bool is_notification = true);
void setCallbacks(NimBLECharacteristicCallbacks* pCallbacks);
void setValue(const uint8_t* data, size_t size);
void setValue(const std::string &value);
/**
* @brief Convenience template to set the characteristic value to <type\>val.
* @param [in] s The value to set.
*/
template<typename T>
void setValue(const T &s) {
void setValue(const T &s) {
setValue((uint8_t*)&s, sizeof(T));
}
std::string toString();
uint16_t getHandle();
size_t getSubscribedCount();
private:

View File

@@ -24,6 +24,9 @@
#include <string>
#include <unordered_set>
#include "nimble/nimble_port.h"
static const char* LOG_TAG = "NimBLEClient";
static NimBLEClientCallbacks defaultCallbacks;
@@ -56,11 +59,10 @@ static NimBLEClientCallbacks defaultCallbacks;
NimBLEClient::NimBLEClient(const NimBLEAddress &peerAddress) : m_peerAddress(peerAddress) {
m_pClientCallbacks = &defaultCallbacks;
m_conn_id = BLE_HS_CONN_HANDLE_NONE;
m_isConnected = false;
m_waitingToConnect = false;
m_connectTimeout = 30000;
m_deleteCallbacks = false;
m_pTaskData = nullptr;
m_connEstablished = false;
m_pConnParams.scan_itvl = 16; // Scan interval in 0.625ms units (NimBLE Default)
m_pConnParams.scan_window = 16; // Scan window in 0.625ms units (NimBLE Default)
@@ -70,6 +72,9 @@ NimBLEClient::NimBLEClient(const NimBLEAddress &peerAddress) : m_peerAddress(pee
m_pConnParams.supervision_timeout = BLE_GAP_INITIAL_SUPERVISION_TIMEOUT; // timeout = 400*10ms = 4000ms
m_pConnParams.min_ce_len = BLE_GAP_INITIAL_CONN_MIN_CE_LEN; // Minimum length of connection event in 0.625ms units
m_pConnParams.max_ce_len = BLE_GAP_INITIAL_CONN_MAX_CE_LEN; // Maximum length of connection event in 0.625ms units
ble_npl_callout_init(&m_dcTimer, nimble_port_get_dflt_eventq(),
NimBLEClient::dcTimerCb, this);
} // NimBLEClient
@@ -89,6 +94,20 @@ NimBLEClient::~NimBLEClient() {
} // ~NimBLEClient
/**
* @brief If we have asked to disconnect and the event does not
* occur within the supervision timeout + added delay, this will
* be called to reset the host in the case of a stalled controller.
*/
void NimBLEClient::dcTimerCb(ble_npl_event *event) {
/* NimBLEClient *pClient = (NimBLEClient*)event->arg;
NIMBLE_LOGC(LOG_TAG, "Timed out disconnecting from %s - resetting host",
std::string(pClient->getPeerAddress()).c_str());
*/
ble_hs_sched_reset(BLE_HS_ECONTROLLER);
}
/**
* @brief Delete all service objects created by this client and clear the vector.
*/
@@ -164,70 +183,119 @@ bool NimBLEClient::connect(const NimBLEAddress &address, bool deleteAttibutes) {
return false;
}
if(ble_gap_conn_active()) {
NIMBLE_LOGE(LOG_TAG, "Connection in progress - must wait.");
if(isConnected() || m_connEstablished || m_pTaskData != nullptr) {
NIMBLE_LOGE(LOG_TAG, "Client busy, connected to %s, id=%d",
std::string(m_peerAddress).c_str(), getConnId());
return false;
}
if(!NimBLEDevice::getScan()->stop()) {
ble_addr_t peerAddr_t;
memcpy(&peerAddr_t.val, address.getNative(),6);
peerAddr_t.type = address.getType();
if(ble_gap_conn_find_by_addr(&peerAddr_t, NULL) == 0) {
NIMBLE_LOGE(LOG_TAG, "A connection to %s already exists",
address.toString().c_str());
return false;
}
if(address == NimBLEAddress("")) {
NIMBLE_LOGE(LOG_TAG, "Invalid peer address;(NULL)");
return false;
} else if(m_peerAddress != address) {
} else {
m_peerAddress = address;
}
ble_addr_t peerAddrt;
memcpy(&peerAddrt.val, m_peerAddress.getNative(),6);
peerAddrt.type = m_peerAddress.getType();
ble_task_data_t taskData = {this, xTaskGetCurrentTaskHandle(), 0, nullptr};
m_pTaskData = &taskData;
int rc = 0;
/* Try to connect the the advertiser. Allow 30 seconds (30000 ms) for
* timeout (default value of m_connectTimeout).
* Loop on BLE_HS_EBUSY if the scan hasn't stopped yet.
*/
do{
rc = ble_gap_connect(BLE_OWN_ADDR_PUBLIC, &peerAddrt, m_connectTimeout, &m_pConnParams,
NimBLEClient::handleGapEvent, this);
if(rc == BLE_HS_EBUSY) {
vTaskDelay(1 / portTICK_PERIOD_MS);
}
}while(rc == BLE_HS_EBUSY);
do {
rc = ble_gap_connect(NimBLEDevice::m_own_addr_type, &peerAddr_t,
m_connectTimeout, &m_pConnParams,
NimBLEClient::handleGapEvent, this);
switch (rc) {
case 0:
break;
if (rc != 0 && rc != BLE_HS_EDONE) {
NIMBLE_LOGE(LOG_TAG, "Error: Failed to connect to device; "
"addr=%s, rc=%d; %s",
std::string(m_peerAddress).c_str(),
rc, NimBLEUtils::returnCodeToString(rc));
case BLE_HS_EBUSY:
// Scan was still running, stop it and try again
if (!NimBLEDevice::getScan()->stop()) {
rc = BLE_HS_EUNKNOWN;
}
break;
case BLE_HS_EDONE:
// A connection to this device already exists, do not connect twice.
NIMBLE_LOGE(LOG_TAG, "Already connected to device; addr=%s",
std::string(m_peerAddress).c_str());
break;
case BLE_HS_EALREADY:
// Already attemting to connect to this device, cancel the previous
// attempt and report failure here so we don't get 2 connections.
NIMBLE_LOGE(LOG_TAG, "Already attempting to connect to %s - cancelling",
std::string(m_peerAddress).c_str());
ble_gap_conn_cancel();
break;
default:
NIMBLE_LOGE(LOG_TAG, "Failed to connect to %s, rc=%d; %s",
std::string(m_peerAddress).c_str(),
rc, NimBLEUtils::returnCodeToString(rc));
break;
}
} while (rc == BLE_HS_EBUSY);
if(rc != 0) {
m_pTaskData = nullptr;
m_waitingToConnect = false;
return false;
}
m_waitingToConnect = true;
// Wait for the connect timeout time +1 second for the connection to complete
if(ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(m_connectTimeout + 1000)) == pdFALSE) {
m_pTaskData = nullptr;
// If a connection was made but no response from MTU exchange; disconnect
if(isConnected()) {
NIMBLE_LOGE(LOG_TAG, "Connect timeout - no response");
disconnect();
} else {
// workaround; if the controller doesn't cancel the connection
// at the timeout, cancel it here.
NIMBLE_LOGE(LOG_TAG, "Connect timeout - cancelling");
ble_gap_conn_cancel();
}
// Wait for the connection to complete.
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
if(taskData.rc != 0){
return false;
} else if(taskData.rc != 0){
NIMBLE_LOGE(LOG_TAG, "Connection failed; status=%d %s",
taskData.rc,
NimBLEUtils::returnCodeToString(taskData.rc));
// If the failure was not a result of a disconnection
// make sure we disconnect now to avoid dangling connections
if(isConnected()) {
disconnect();
}
return false;
} else {
NIMBLE_LOGI(LOG_TAG, "Connection established");
}
if(deleteAttibutes) {
deleteServices();
}
m_connEstablished = true;
m_pClientCallbacks->onConnect(this);
NIMBLE_LOGD(LOG_TAG, "<< connect()");
return true;
// Check if still connected before returning
return isConnected();
} // connect
@@ -268,12 +336,39 @@ bool NimBLEClient::secureConnection() {
int NimBLEClient::disconnect(uint8_t reason) {
NIMBLE_LOGD(LOG_TAG, ">> disconnect()");
int rc = 0;
if(m_isConnected){
rc = ble_gap_terminate(m_conn_id, reason);
if(rc != 0){
NIMBLE_LOGE(LOG_TAG, "ble_gap_terminate failed: rc=%d %s", rc,
NimBLEUtils::returnCodeToString(rc));
if(isConnected()) {
// If the timer was already started, ignore this call.
if(ble_npl_callout_is_active(&m_dcTimer)) {
NIMBLE_LOGI(LOG_TAG, "Already disconnecting, timer started");
return BLE_HS_EALREADY;
}
ble_gap_conn_desc desc;
if(ble_gap_conn_find(m_conn_id, &desc) != 0){
NIMBLE_LOGI(LOG_TAG, "Connection ID not found");
return BLE_HS_EALREADY;
}
// We use a timer to detect a controller error in the event that it does
// not inform the stack when disconnection is complete.
// This is a common error in certain esp-idf versions.
// The disconnect timeout time is the supervison timeout time + 1 second.
// In the case that the event happenss shortly after the supervision timeout
// we don't want to prematurely reset the host.
ble_npl_time_t ticks;
ble_npl_time_ms_to_ticks((desc.supervision_timeout + 100) * 10, &ticks);
ble_npl_callout_reset(&m_dcTimer, ticks);
rc = ble_gap_terminate(m_conn_id, reason);
if (rc != 0) {
if(rc != BLE_HS_EALREADY) {
ble_npl_callout_stop(&m_dcTimer);
}
NIMBLE_LOGE(LOG_TAG, "ble_gap_terminate failed: rc=%d %s",
rc, NimBLEUtils::returnCodeToString(rc));
}
} else {
NIMBLE_LOGD(LOG_TAG, "Not connected to any peers");
}
NIMBLE_LOGD(LOG_TAG, "<< disconnect()");
@@ -283,12 +378,12 @@ int NimBLEClient::disconnect(uint8_t reason) {
/**
* @brief Set the connection paramaters to use when connecting to a server.
* @param [in] minInterval minimum connection interval in 0.625ms units.
* @param [in] maxInterval maximum connection interval in 0.625ms units.
* @param [in] latency number of packets allowed to skip (extends max interval)
* @param [in] timeout the timeout time in 10ms units before disconnecting
* @param [in] scanInterval the scan interval to use when attempting to connect in 0.625ms units.
* @param [in] scanWindow the scan window to use when attempting to connect in 0.625ms units.
* @param [in] minInterval The minimum connection interval in 1.25ms units.
* @param [in] maxInterval The maximum connection interval in 1.25ms units.
* @param [in] latency The number of packets allowed to skip (extends max interval).
* @param [in] timeout The timeout time in 10ms units before disconnecting.
* @param [in] scanInterval The scan interval to use when attempting to connect in 0.625ms units.
* @param [in] scanWindow The scan window to use when attempting to connect in 0.625ms units.
*/
void NimBLEClient::setConnectionParams(uint16_t minInterval, uint16_t maxInterval,
uint16_t latency, uint16_t timeout,
@@ -315,10 +410,10 @@ void NimBLEClient::setConnectionParams(uint16_t minInterval, uint16_t maxInterva
/**
* @brief Update the connection parameters:
* * Can only be used after a connection has been established.
* @param [in] minInterval minimum connection interval in 0.625ms units.
* @param [in] maxInterval maximum connection interval in 0.625ms units.
* @param [in] latency number of packets allowed to skip (extends max interval)
* @param [in] timeout the timeout time in 10ms units before disconnecting
* @param [in] minInterval The minimum connection interval in 1.25ms units.
* @param [in] maxInterval The maximum connection interval in 1.25ms units.
* @param [in] latency The number of packets allowed to skip (extends max interval).
* @param [in] timeout The timeout time in 10ms units before disconnecting.
*/
void NimBLEClient::updateConnParams(uint16_t minInterval, uint16_t maxInterval,
uint16_t latency, uint16_t timeout)
@@ -454,6 +549,16 @@ NimBLERemoteService* NimBLEClient::getService(const NimBLEUUID &uuid) {
if(m_servicesVector.size() > prev_size) {
return m_servicesVector.back();
}
// If the request was successful but 16/32 bit service not found
// try again with the 128 bit uuid.
if(uuid.bitSize() == BLE_UUID_TYPE_16 ||
uuid.bitSize() == BLE_UUID_TYPE_32)
{
NimBLEUUID uuid128(uuid);
uuid128.to128();
return getService(uuid128);
}
}
NIMBLE_LOGD(LOG_TAG, "<< getService: not found");
@@ -510,7 +615,7 @@ bool NimBLEClient::retrieveServices(const NimBLEUUID *uuid_filter) {
NIMBLE_LOGD(LOG_TAG, ">> retrieveServices");
if(!m_isConnected){
if(!isConnected()){
NIMBLE_LOGE(LOG_TAG, "Disconnected, could not retrieve services -aborting");
return false;
}
@@ -618,10 +723,11 @@ std::string NimBLEClient::getValue(const NimBLEUUID &serviceUUID, const NimBLEUU
* @param [in] serviceUUID The service that owns the characteristic.
* @param [in] characteristicUUID The characteristic whose value we wish to write.
* @param [in] value The value to write to the characteristic.
* @param [in] response If true, uses write with response operation.
* @returns true if successful otherwise false
*/
bool NimBLEClient::setValue(const NimBLEUUID &serviceUUID, const NimBLEUUID &characteristicUUID,
const std::string &value)
const std::string &value, bool response)
{
NIMBLE_LOGD(LOG_TAG, ">> setValue: serviceUUID: %s, characteristicUUID: %s",
serviceUUID.toString().c_str(), characteristicUUID.toString().c_str());
@@ -632,7 +738,7 @@ bool NimBLEClient::setValue(const NimBLEUUID &serviceUUID, const NimBLEUUID &cha
if(pService != nullptr) {
NimBLERemoteCharacteristic* pChar = pService->getCharacteristic(characteristicUUID);
if(pChar != nullptr) {
ret = pChar->writeValue(value);
ret = pChar->writeValue(value, response);
}
}
@@ -641,6 +747,31 @@ bool NimBLEClient::setValue(const NimBLEUUID &serviceUUID, const NimBLEUUID &cha
} // setValue
/**
* @brief Get the remote characteristic with the specified handle.
* @param [in] handle The handle of the desired characteristic.
* @returns The matching remote characteristic, nullptr otherwise.
*/
NimBLERemoteCharacteristic* NimBLEClient::getCharacteristic(const uint16_t handle)
{
NimBLERemoteService *pService = nullptr;
for(auto it = m_servicesVector.begin(); it != m_servicesVector.end(); ++it) {
if ((*it)->getStartHandle() <= handle && handle <= (*it)->getEndHandle()) {
pService = *it;
break;
}
}
if (pService != nullptr) {
for (auto it = pService->begin(); it != pService->end(); ++it) {
if ((*it)->getHandle() == handle) {
return *it;
}
}
}
return nullptr;
}
/**
* @brief Get the current mtu of this connection.
@@ -656,7 +787,8 @@ uint16_t NimBLEClient::getMTU() {
* @param [in] event The event structure sent by the NimBLE stack.
* @param [in] arg A pointer to the client instance that registered for this callback.
*/
/*STATIC*/ int NimBLEClient::handleGapEvent(struct ble_gap_event *event, void *arg) {
/*STATIC*/
int NimBLEClient::handleGapEvent(struct ble_gap_event *event, void *arg) {
NimBLEClient* client = (NimBLEClient*)arg;
int rc;
@@ -665,61 +797,67 @@ uint16_t NimBLEClient::getMTU() {
switch(event->type) {
case BLE_GAP_EVENT_DISCONNECT: {
if(!client->m_isConnected)
return 0;
if(client->m_conn_id != event->disconnect.conn.conn_handle)
return 0;
client->m_isConnected = false;
client->m_waitingToConnect=false;
// Remove the device from ignore list so we will scan it again
NimBLEDevice::removeIgnored(client->m_peerAddress);
NIMBLE_LOGI(LOG_TAG, "disconnect; reason=%d, %s", event->disconnect.reason,
NimBLEUtils::returnCodeToString(event->disconnect.reason));
rc = event->disconnect.reason;
// If Host reset tell the device now before returning to prevent
// any errors caused by calling host functions before resyncing.
switch(event->disconnect.reason) {
case BLE_HS_ETIMEOUT_HCI:
case BLE_HS_EOS:
switch(rc) {
case BLE_HS_ECONTROLLER:
case BLE_HS_ETIMEOUT_HCI:
case BLE_HS_ENOTSYNCED:
NIMBLE_LOGC(LOG_TAG, "Disconnect - host reset, rc=%d", event->disconnect.reason);
NimBLEDevice::onReset(event->disconnect.reason);
case BLE_HS_EOS:
NIMBLE_LOGC(LOG_TAG, "Disconnect - host reset, rc=%d", rc);
NimBLEDevice::onReset(rc);
break;
default:
// Check that the event is for this client.
if(client->m_conn_id != event->disconnect.conn.conn_handle) {
return 0;
}
break;
}
//client->m_conn_id = BLE_HS_CONN_HANDLE_NONE;
// Stop the disconnect timer since we are now disconnected.
ble_npl_callout_stop(&client->m_dcTimer);
// Remove the device from ignore list so we will scan it again
NimBLEDevice::removeIgnored(client->m_peerAddress);
// No longer connected, clear the connection ID.
client->m_conn_id = BLE_HS_CONN_HANDLE_NONE;
// If we received a connected event but did not get established (no PDU)
// then a disconnect event will be sent but we should not send it to the
// app for processing. Instead we will ensure the task is released
// and report the error.
if(!client->m_connEstablished)
break;
NIMBLE_LOGI(LOG_TAG, "disconnect; reason=%d, %s",
rc, NimBLEUtils::returnCodeToString(rc));
client->m_connEstablished = false;
client->m_pClientCallbacks->onDisconnect(client);
rc = event->disconnect.reason;
break;
} // BLE_GAP_EVENT_DISCONNECT
case BLE_GAP_EVENT_CONNECT: {
if(!client->m_waitingToConnect)
// If we aren't waiting for this connection response
// we should drop the connection immediately.
if(client->isConnected() || client->m_pTaskData == nullptr) {
ble_gap_terminate(event->connect.conn_handle, BLE_ERR_REM_USER_CONN_TERM);
return 0;
}
//if(client->m_conn_id != BLE_HS_CONN_HANDLE_NONE)
// return 0;
client->m_waitingToConnect=false;
if (event->connect.status == 0) {
client->m_isConnected = true;
NIMBLE_LOGD(LOG_TAG, "Connection established");
rc = event->connect.status;
if (rc == 0) {
NIMBLE_LOGI(LOG_TAG, "Connected event");
client->m_conn_id = event->connect.conn_handle;
rc = ble_gattc_exchange_mtu(client->m_conn_id, NULL,NULL);
if(rc != 0) {
NIMBLE_LOGE(LOG_TAG, "ble_gattc_exchange_mtu: rc=%d %s",rc,
NimBLEUtils::returnCodeToString(rc));
NIMBLE_LOGE(LOG_TAG, "MTU exchange error; rc=%d %s",
rc, NimBLEUtils::returnCodeToString(rc));
break;
}
@@ -727,14 +865,10 @@ uint16_t NimBLEClient::getMTU() {
// scanning since we are already connected to it
NimBLEDevice::addIgnored(client->m_peerAddress);
} else {
NIMBLE_LOGE(LOG_TAG, "Error: Connection failed; status=%d %s",
event->connect.status,
NimBLEUtils::returnCodeToString(event->connect.status));
client->m_isConnected = false;
rc = event->connect.status;
client->m_conn_id = BLE_HS_CONN_HANDLE_NONE;
break;
}
return 0;
} // BLE_GAP_EVENT_CONNECT
@@ -742,7 +876,14 @@ uint16_t NimBLEClient::getMTU() {
if(client->m_conn_id != event->notify_rx.conn_handle)
return 0;
NIMBLE_LOGD(LOG_TAG, "Notify Recieved for handle: %d",event->notify_rx.attr_handle);
// If a notification comes before this flag is set we might
// access a vector while it is being cleared in connect()
if(!client->m_connEstablished) {
return 0;
}
NIMBLE_LOGD(LOG_TAG, "Notify Recieved for handle: %d",
event->notify_rx.attr_handle);
for(auto &it: client->m_servicesVector) {
// Dont waste cycles searching services without this handle in its range
@@ -752,8 +893,8 @@ uint16_t NimBLEClient::getMTU() {
auto cVector = &it->m_characteristicVector;
NIMBLE_LOGD(LOG_TAG, "checking service %s for handle: %d",
it->getUUID().toString().c_str(),
event->notify_rx.attr_handle);
it->getUUID().toString().c_str(),
event->notify_rx.attr_handle);
auto characteristic = cVector->cbegin();
for(; characteristic != cVector->cend(); ++characteristic) {
@@ -762,16 +903,19 @@ uint16_t NimBLEClient::getMTU() {
}
if(characteristic != cVector->cend()) {
NIMBLE_LOGD(LOG_TAG, "Got Notification for characteristic %s", (*characteristic)->toString().c_str());
NIMBLE_LOGD(LOG_TAG, "Got Notification for characteristic %s",
(*characteristic)->toString().c_str());
time_t t = time(nullptr);
portENTER_CRITICAL(&(*characteristic)->m_valMux);
(*characteristic)->m_value = std::string((char *)event->notify_rx.om->om_data, event->notify_rx.om->om_len);
(*characteristic)->m_timestamp = time(nullptr);
(*characteristic)->m_value = std::string((char *)event->notify_rx.om->om_data,
event->notify_rx.om->om_len);
(*characteristic)->m_timestamp = t;
portEXIT_CRITICAL(&(*characteristic)->m_valMux);
if ((*characteristic)->m_notifyCallback != nullptr) {
NIMBLE_LOGD(LOG_TAG, "Invoking callback for notification on characteristic %s",
(*characteristic)->toString().c_str());
(*characteristic)->toString().c_str());
(*characteristic)->m_notifyCallback(*characteristic, event->notify_rx.om->om_data,
event->notify_rx.om->om_len,
!event->notify_rx.indication);
@@ -790,10 +934,10 @@ uint16_t NimBLEClient::getMTU() {
}
NIMBLE_LOGD(LOG_TAG, "Peer requesting to update connection parameters");
NIMBLE_LOGD(LOG_TAG, "MinInterval: %d, MaxInterval: %d, Latency: %d, Timeout: %d",
event->conn_update_req.peer_params->itvl_min,
event->conn_update_req.peer_params->itvl_max,
event->conn_update_req.peer_params->latency,
event->conn_update_req.peer_params->supervision_timeout);
event->conn_update_req.peer_params->itvl_min,
event->conn_update_req.peer_params->itvl_max,
event->conn_update_req.peer_params->latency,
event->conn_update_req.peer_params->supervision_timeout);
rc = client->m_pClientCallbacks->onConnParamsUpdateRequest(client,
event->conn_update_req.peer_params) ? 0 : BLE_ERR_CONN_PARMS;
@@ -827,7 +971,9 @@ uint16_t NimBLEClient::getMTU() {
return 0;
}
if(event->enc_change.status == 0 || event->enc_change.status == (BLE_HS_ERR_HCI_BASE + BLE_ERR_PINKEY_MISSING)) {
if(event->enc_change.status == 0 ||
event->enc_change.status == (BLE_HS_ERR_HCI_BASE + BLE_ERR_PINKEY_MISSING))
{
struct ble_gap_conn_desc desc;
rc = ble_gap_conn_find(event->enc_change.conn_handle, &desc);
assert(rc == 0);
@@ -922,7 +1068,9 @@ uint16_t NimBLEClient::getMTU() {
if(client->m_pTaskData != nullptr) {
client->m_pTaskData->rc = rc;
xTaskNotifyGive(client->m_pTaskData->task);
if(client->m_pTaskData->task) {
xTaskNotifyGive(client->m_pTaskData->task);
}
client->m_pTaskData = nullptr;
}
@@ -935,7 +1083,7 @@ uint16_t NimBLEClient::getMTU() {
* @return True if we are connected and false if we are not connected.
*/
bool NimBLEClient::isConnected() {
return m_isConnected;
return m_conn_id != BLE_HS_CONN_HANDLE_NONE;
} // isConnected

View File

@@ -30,6 +30,7 @@
#include <string>
class NimBLERemoteService;
class NimBLERemoteCharacteristic;
class NimBLEClientCallbacks;
class NimBLEAdvertisedDevice;
@@ -54,7 +55,8 @@ public:
size_t deleteService(const NimBLEUUID &uuid);
std::string getValue(const NimBLEUUID &serviceUUID, const NimBLEUUID &characteristicUUID);
bool setValue(const NimBLEUUID &serviceUUID, const NimBLEUUID &characteristicUUID,
const std::string &value);
const std::string &value, bool response = false);
NimBLERemoteCharacteristic* getCharacteristic(const uint16_t handle);
bool isConnected();
void setClientCallbacks(NimBLEClientCallbacks *pClientCallbacks,
bool deleteCallbacks = true);
@@ -82,16 +84,17 @@ private:
const struct ble_gatt_error *error,
const struct ble_gatt_svc *service,
void *arg);
static void dcTimerCb(ble_npl_event *event);
bool retrieveServices(const NimBLEUUID *uuid_filter = nullptr);
NimBLEAddress m_peerAddress;
uint16_t m_conn_id;
bool m_isConnected;
bool m_waitingToConnect;
bool m_connEstablished;
bool m_deleteCallbacks;
int32_t m_connectTimeout;
NimBLEClientCallbacks* m_pClientCallbacks;
ble_task_data_t *m_pTaskData;
ble_task_data_t* m_pTaskData;
ble_npl_callout m_dcTimer;
std::vector<NimBLERemoteService*> m_servicesVector;

View File

@@ -122,11 +122,17 @@ uint8_t* NimBLEDescriptor::getValue() {
return m_value.attr_value;
} // getValue
/**
* @brief Get the value of this descriptor as a string.
* @return A std::string instance containing a copy of the descriptor's value.
*/
std::string NimBLEDescriptor::getStringValue() {
return std::string((char *) m_value.attr_value, m_value.attr_len);
}
int NimBLEDescriptor::handleGapEvent(uint16_t conn_handle, uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt,
void *arg)
{
void *arg) {
const ble_uuid_t *uuid;
int rc;
NimBLEDescriptor* pDescriptor = (NimBLEDescriptor*)arg;

View File

@@ -44,20 +44,23 @@ class NimBLEDescriptorCallbacks;
class NimBLEDescriptor {
public:
uint16_t getHandle();
size_t getLength();
NimBLEUUID getUUID();
uint8_t* getValue();
void setCallbacks(NimBLEDescriptorCallbacks* pCallbacks);
void setValue(const uint8_t* data, size_t size);
void setValue(const std::string &value);
std::string toString();
void setCallbacks(NimBLEDescriptorCallbacks* pCallbacks);
size_t getLength();
uint8_t* getValue();
std::string getStringValue();
void setValue(const uint8_t* data, size_t size);
void setValue(const std::string &value);
/**
* @brief Convenience template to set the descriptor value to <type\>val.
* @param [in] s The value to set.
*/
template<typename T>
void setValue(const T &s) {
void setValue(const T &s) {
setValue((uint8_t*)&s, sizeof(T));
}

View File

@@ -25,6 +25,7 @@
#include "nimble/nimble_port.h"
#include "nimble/nimble_port_freertos.h"
#include "host/ble_hs.h"
#include "host/ble_hs_pvcy.h"
#include "host/util/util.h"
#include "services/gap/ble_svc_gap.h"
#include "services/gatt/ble_svc_gatt.h"
@@ -60,6 +61,9 @@ std::list <NimBLEClient*> NimBLEDevice::m_cList;
#endif
std::list <NimBLEAddress> NimBLEDevice::m_ignoreList;
NimBLESecurityCallbacks* NimBLEDevice::m_securityCallbacks = nullptr;
uint8_t NimBLEDevice::m_own_addr_type = BLE_OWN_ADDR_PUBLIC;
uint16_t NimBLEDevice::m_scanDuplicateSize = CONFIG_BTDM_SCAN_DUPL_CACHE_SIZE;
uint8_t NimBLEDevice::m_scanFilterMode = CONFIG_BTDM_SCAN_DUPL_TYPE;
/**
@@ -144,8 +148,8 @@ void NimBLEDevice::stopAdvertising() {
#if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
/* STATIC */ NimBLEClient* NimBLEDevice::createClient(NimBLEAddress peerAddress) {
if(m_cList.size() >= NIMBLE_MAX_CONNECTIONS) {
NIMBLE_LOGW("Number of clients exceeds Max connections. Max=(%d)",
NIMBLE_MAX_CONNECTIONS);
NIMBLE_LOGW(LOG_TAG,"Number of clients exceeds Max connections. Cur=%d Max=%d",
m_cList.size(), NIMBLE_MAX_CONNECTIONS);
}
NimBLEClient* pClient = new NimBLEClient(peerAddress);
@@ -165,26 +169,31 @@ void NimBLEDevice::stopAdvertising() {
return false;
}
// Set the connection established flag to false to stop notifications
// from accessing the attribute vectors while they are being deleted.
pClient->m_connEstablished = false;
int rc =0;
if(pClient->m_isConnected) {
if(pClient->isConnected()) {
rc = pClient->disconnect();
if (rc != 0 && rc != BLE_HS_EALREADY && rc != BLE_HS_ENOTCONN) {
return false;
}
while(pClient->m_isConnected) {
vTaskDelay(10);
while(pClient->isConnected()) {
taskYIELD();
}
}
// Since we set the flag to false the app will not get a callback
// in the disconnect event so we call it here for good measure.
pClient->m_pClientCallbacks->onDisconnect(pClient);
if(pClient->m_waitingToConnect) {
} else if(pClient->m_pTaskData != nullptr) {
rc = ble_gap_conn_cancel();
if (rc != 0 && rc != BLE_HS_EALREADY) {
return false;
}
while(pClient->m_waitingToConnect) {
vTaskDelay(10);
while(pClient->m_pTaskData != nullptr) {
taskYIELD();
}
}
@@ -296,7 +305,7 @@ void NimBLEDevice::stopAdvertising() {
/**
* @brief Set the transmission power.
* @brief Get the transmission power.
* @param [in] powerType The power level to set, can be one of:
* * ESP_BLE_PWR_TYPE_CONN_HDL0 = 0, For connection handle 0
* * ESP_BLE_PWR_TYPE_CONN_HDL1 = 1, For connection handle 1
@@ -335,7 +344,7 @@ void NimBLEDevice::stopAdvertising() {
default:
return BLE_HS_ADV_TX_PWR_LVL_AUTO;
}
} // setPower
} // getPower
/**
@@ -393,6 +402,53 @@ void NimBLEDevice::stopAdvertising() {
}
/**
* @brief Set the duplicate filter cache size for filtering scanned devices.
* @param [in] cacheSize The number of advertisements filtered before the cache is reset.\n
* Range is 10-1000, a larger value will reduce how often the same devices are reported.
* @details Must only be called before calling NimBLEDevice::init.
*/
/*STATIC*/
void NimBLEDevice::setScanDuplicateCacheSize(uint16_t cacheSize) {
if(initialized) {
NIMBLE_LOGE(LOG_TAG, "Cannot change scan cache size while initialized");
return;
} else if(cacheSize > 1000 || cacheSize <10) {
NIMBLE_LOGE(LOG_TAG, "Invalid scan cache size; min=10 max=1000");
return;
}
m_scanDuplicateSize = cacheSize;
}
/**
* @brief Set the duplicate filter mode for filtering scanned devices.
* @param [in] mode One of three possible options:
* * CONFIG_BTDM_SCAN_DUPL_TYPE_DEVICE (0) (default)\n
Filter by device address only, advertisements from the same address will be reported only once.
* * CONFIG_BTDM_SCAN_DUPL_TYPE_DATA (1)\n
Filter by data only, advertisements with the same data will only be reported once,\n
even from different addresses.
* * CONFIG_BTDM_SCAN_DUPL_TYPE_DATA_DEVICE (2)\n
Filter by address and data, advertisements from the same address will be reported only once,\n
except if the data in the advertisement has changed, then it will be reported again.
* @details Must only be called before calling NimBLEDevice::init.
*/
/*STATIC*/
void NimBLEDevice::setScanFilterMode(uint8_t mode) {
if(initialized) {
NIMBLE_LOGE(LOG_TAG, "Cannot change scan duplicate type while initialized");
return;
} else if(mode > 2) {
NIMBLE_LOGE(LOG_TAG, "Invalid scan duplicate type");
return;
}
m_scanFilterMode = mode;
}
/**
* @brief Host reset, we pass the message so we don't make calls until resynced.
* @param [in] reason The reason code for the reset.
@@ -405,30 +461,16 @@ void NimBLEDevice::stopAdvertising() {
m_synced = false;
#if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
if(m_pScan != nullptr) {
m_pScan->onHostReset();
}
#endif
/* Not needed
if(m_pServer != nullptr) {
m_pServer->onHostReset();
}
for(auto it = m_cList.cbegin(); it != m_cList.cend(); ++it) {
(*it)->onHostReset();
}
*/
#if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
if(m_bleAdvertising != nullptr) {
m_bleAdvertising->onHostReset();
}
#endif
NIMBLE_LOGC(LOG_TAG, "Resetting state; reason=%d, %s", reason,
NimBLEUtils::returnCodeToString(reason));
#if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
if(initialized) {
if(m_pScan != nullptr) {
m_pScan->onHostReset();
}
}
#endif
} // onReset
@@ -448,20 +490,22 @@ void NimBLEDevice::stopAdvertising() {
int rc = ble_hs_util_ensure_addr(0);
assert(rc == 0);
// Yield for houskeeping before returning to operations.
// Occasionally triggers exception without.
taskYIELD();
m_synced = true;
if(initialized) {
#if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
if(m_pScan != nullptr) {
// Restart scanning with the last values sent, allow to clear results.
m_pScan->start(m_pScan->m_duration, m_pScan->m_scanCompleteCB);
m_pScan->onHostSync();
}
#endif
#if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
if(m_bleAdvertising != nullptr) {
// Restart advertisng, parameters should already be set.
m_bleAdvertising->start();
m_bleAdvertising->onHostSync();
}
#endif
}
@@ -504,8 +548,17 @@ void NimBLEDevice::stopAdvertising() {
ESP_ERROR_CHECK(errRc);
ESP_ERROR_CHECK(esp_nimble_hci_and_controller_init());
esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT);
esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
bt_cfg.mode = ESP_BT_MODE_BLE;
bt_cfg.ble_max_conn = CONFIG_BT_NIMBLE_MAX_CONNECTIONS;
bt_cfg.normal_adv_size = m_scanDuplicateSize;
bt_cfg.scan_duplicate_type = m_scanFilterMode;
ESP_ERROR_CHECK(esp_bt_controller_init(&bt_cfg));
ESP_ERROR_CHECK(esp_bt_controller_enable(ESP_BT_MODE_BLE));
ESP_ERROR_CHECK(esp_nimble_hci_init());
nimble_port_init();
// Setup callbacks for host events
@@ -705,6 +758,35 @@ void NimBLEDevice::setSecurityCallbacks(NimBLESecurityCallbacks* callbacks) {
} // setSecurityCallbacks
/**
* @brief Set the own address type.
* @param [in] own_addr_type Own Bluetooth Device address type.\n
* The available bits are defined as:
* * 0x00: BLE_OWN_ADDR_PUBLIC
* * 0x01: BLE_OWN_ADDR_RANDOM
* * 0x02: BLE_OWN_ADDR_RPA_PUBLIC_DEFAULT
* * 0x03: BLE_OWN_ADDR_RPA_RANDOM_DEFAULT
* @param [in] useNRPA If true, and address type is random, uses a non-resolvable random address.
*/
void NimBLEDevice::setOwnAddrType(uint8_t own_addr_type, bool useNRPA) {
m_own_addr_type = own_addr_type;
switch (own_addr_type) {
case BLE_OWN_ADDR_PUBLIC:
ble_hs_pvcy_rpa_config(NIMBLE_HOST_DISABLE_PRIVACY);
break;
case BLE_OWN_ADDR_RANDOM:
setSecurityInitKey(BLE_SM_PAIR_KEY_DIST_ENC | BLE_SM_PAIR_KEY_DIST_ID);
ble_hs_pvcy_rpa_config(useNRPA ? NIMBLE_HOST_ENABLE_NRPA : NIMBLE_HOST_ENABLE_RPA);
break;
case BLE_OWN_ADDR_RPA_PUBLIC_DEFAULT:
case BLE_OWN_ADDR_RPA_RANDOM_DEFAULT:
setSecurityInitKey(BLE_SM_PAIR_KEY_DIST_ENC | BLE_SM_PAIR_KEY_DIST_ID);
ble_hs_pvcy_rpa_config(NIMBLE_HOST_ENABLE_RPA);
break;
}
} // setOwnAddrType
/**
* @brief Start the connection securing and authorization for this connection.
* @param conn_id The connection id of the peer device.

View File

@@ -116,12 +116,15 @@ public:
static void setSecurityPasskey(uint32_t pin);
static uint32_t getSecurityPasskey();
static void setSecurityCallbacks(NimBLESecurityCallbacks* pCallbacks);
static void setOwnAddrType(uint8_t own_addr_type, bool useNRPA=false);
static int startSecurity(uint16_t conn_id);
static int setMTU(uint16_t mtu);
static uint16_t getMTU();
static bool isIgnored(const NimBLEAddress &address);
static void addIgnored(const NimBLEAddress &address);
static void removeIgnored(const NimBLEAddress &address);
static void setScanDuplicateCacheSize(uint16_t cacheSize);
static void setScanFilterMode(uint8_t type);
#if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
static NimBLEAdvertising* getAdvertising();
@@ -182,6 +185,9 @@ private:
static uint32_t m_passkey;
static ble_gap_event_listener m_listener;
static gap_event_handler m_customGapHandler;
static uint8_t m_own_addr_type;
static uint16_t m_scanDuplicateSize;
static uint8_t m_scanFilterMode;
};

251
src/NimBLEHIDDevice.cpp Normal file
View File

@@ -0,0 +1,251 @@
/*
* NimBLEHIDDevice.cpp
*
* Created: on Oct 06 2020
* Author wakwak-koba
*
* Originally:
*
* BLEHIDDevice.cpp
*
* Created on: Jan 03, 2018
* Author: chegewara
*/
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include "nimconfig.h"
#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#include "NimBLEHIDDevice.h"
#include "NimBLE2904.h"
/**
* @brief Construct a default NimBLEHIDDevice object.
* @param [in] server A pointer to the server instance this HID Device will use.
*/
NimBLEHIDDevice::NimBLEHIDDevice(NimBLEServer* server) {
/*
* Here we create mandatory services described in bluetooth specification
*/
m_deviceInfoService = server->createService(NimBLEUUID((uint16_t) 0x180a));
m_hidService = server->createService(NimBLEUUID((uint16_t) 0x1812), 40);
m_batteryService = server->createService(NimBLEUUID((uint16_t) 0x180f));
/*
* Mandatory characteristic for device info service
*/
m_pnpCharacteristic = m_deviceInfoService->createCharacteristic((uint16_t) 0x2a50, NIMBLE_PROPERTY::READ);
/*
* Mandatory characteristics for HID service
*/
m_hidInfoCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a4a, NIMBLE_PROPERTY::READ);
m_reportMapCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a4b, NIMBLE_PROPERTY::READ);
m_hidControlCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a4c, NIMBLE_PROPERTY::WRITE_NR);
m_protocolModeCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a4e, NIMBLE_PROPERTY::WRITE_NR | NIMBLE_PROPERTY::READ);
/*
* Mandatory battery level characteristic with notification and presence descriptor
*/
m_batteryLevelCharacteristic = m_batteryService->createCharacteristic((uint16_t) 0x2a19, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::NOTIFY);
NimBLE2904* batteryLevelDescriptor = (NimBLE2904*)m_batteryLevelCharacteristic->createDescriptor((uint16_t) 0x2904);
batteryLevelDescriptor->setFormat(NimBLE2904::FORMAT_UINT8);
batteryLevelDescriptor->setNamespace(1);
batteryLevelDescriptor->setUnit(0x27ad);
/*
* This value is setup here because its default value in most usage cases, its very rare to use boot mode
* and we want to simplify library using as much as possible
*/
const uint8_t pMode[] = { 0x01 };
protocolMode()->setValue((uint8_t*) pMode, 1);
}
NimBLEHIDDevice::~NimBLEHIDDevice() {
}
/**
* @brief Set the report map data formatting information.
* @param [in] map A pointer to an array with the values to set.
* @param [in] size The number of values in the array.
*/
void NimBLEHIDDevice::reportMap(uint8_t* map, uint16_t size) {
m_reportMapCharacteristic->setValue(map, size);
}
/**
* @brief Start the HID device services.\n
* This function called when all the services have been created.
*/
void NimBLEHIDDevice::startServices() {
m_deviceInfoService->start();
m_hidService->start();
m_batteryService->start();
}
/**
* @brief Create a manufacturer characteristic (this characteristic is optional).
*/
NimBLECharacteristic* NimBLEHIDDevice::manufacturer() {
m_manufacturerCharacteristic = m_deviceInfoService->createCharacteristic((uint16_t) 0x2a29, NIMBLE_PROPERTY::READ);
return m_manufacturerCharacteristic;
}
/**
* @brief Set manufacturer name
* @param [in] name The manufacturer name of this HID device.
*/
void NimBLEHIDDevice::manufacturer(std::string name) {
m_manufacturerCharacteristic->setValue(name);
}
/**
* @brief Sets the Plug n Play characterisc value.
* @param [in] sig The vendor ID source number.
* @param [in] vid The vendor ID number.
* @param [in] pid The product ID number.
* @param [in] version The produce version number.
*/
void NimBLEHIDDevice::pnp(uint8_t sig, uint16_t vid, uint16_t pid, uint16_t version) {
uint8_t pnp[] = { sig, (uint8_t) (vid >> 8), (uint8_t) vid, (uint8_t) (pid >> 8), (uint8_t) pid, (uint8_t) (version >> 8), (uint8_t) version };
m_pnpCharacteristic->setValue(pnp, sizeof(pnp));
}
/**
* @brief Sets the HID Information characteristic value.
* @param [in] country The country code for the device.
* @param [in] flags The HID Class Specification release number to use.
*/
void NimBLEHIDDevice::hidInfo(uint8_t country, uint8_t flags) {
uint8_t info[] = { 0x11, 0x1, country, flags };
m_hidInfoCharacteristic->setValue(info, sizeof(info));
}
/**
* @brief Create input report characteristic
* @param [in] reportID input report ID, the same as in report map for input object related to the characteristic
* @return pointer to new input report characteristic
*/
NimBLECharacteristic* NimBLEHIDDevice::inputReport(uint8_t reportID) {
NimBLECharacteristic* inputReportCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a4d, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::NOTIFY | NIMBLE_PROPERTY::READ_ENC);
NimBLEDescriptor* inputReportDescriptor = inputReportCharacteristic->createDescriptor((uint16_t) 0x2908, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::READ_ENC);
uint8_t desc1_val[] = { reportID, 0x01 };
inputReportDescriptor->setValue((uint8_t*) desc1_val, 2);
return inputReportCharacteristic;
}
/**
* @brief Create output report characteristic
* @param [in] reportID Output report ID, the same as in report map for output object related to the characteristic
* @return Pointer to new output report characteristic
*/
NimBLECharacteristic* NimBLEHIDDevice::outputReport(uint8_t reportID) {
NimBLECharacteristic* outputReportCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a4d, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::WRITE_NR | NIMBLE_PROPERTY::READ_ENC | NIMBLE_PROPERTY::WRITE_ENC);
NimBLEDescriptor* outputReportDescriptor = outputReportCharacteristic->createDescriptor((uint16_t) 0x2908, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::READ_ENC | NIMBLE_PROPERTY::WRITE_ENC);
uint8_t desc1_val[] = { reportID, 0x02 };
outputReportDescriptor->setValue((uint8_t*) desc1_val, 2);
return outputReportCharacteristic;
}
/**
* @brief Create feature report characteristic.
* @param [in] reportID Feature report ID, the same as in report map for feature object related to the characteristic
* @return Pointer to new feature report characteristic
*/
NimBLECharacteristic* NimBLEHIDDevice::featureReport(uint8_t reportID) {
NimBLECharacteristic* featureReportCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a4d, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::READ_ENC | NIMBLE_PROPERTY::WRITE_ENC);
NimBLEDescriptor* featureReportDescriptor = featureReportCharacteristic->createDescriptor((uint16_t) 0x2908, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::READ_ENC | NIMBLE_PROPERTY::WRITE_ENC);
uint8_t desc1_val[] = { reportID, 0x03 };
featureReportDescriptor->setValue((uint8_t*) desc1_val, 2);
return featureReportCharacteristic;
}
/**
* @brief Creates a keyboard boot input report characteristic
*/
NimBLECharacteristic* NimBLEHIDDevice::bootInput() {
return m_hidService->createCharacteristic((uint16_t) 0x2a22, NIMBLE_PROPERTY::NOTIFY);
}
/**
* @brief Create a keyboard boot output report characteristic
*/
NimBLECharacteristic* NimBLEHIDDevice::bootOutput() {
return m_hidService->createCharacteristic((uint16_t) 0x2a32, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::WRITE_NR);
}
/**
* @brief Returns a pointer to the HID control point characteristic.
*/
NimBLECharacteristic* NimBLEHIDDevice::hidControl() {
return m_hidControlCharacteristic;
}
/**
* @brief Returns a pointer to the protocol mode characteristic.
*/
NimBLECharacteristic* NimBLEHIDDevice::protocolMode() {
return m_protocolModeCharacteristic;
}
/**
* @brief Set the battery level characteristic value.
* @param [in] level The battery level value.
*/
void NimBLEHIDDevice::setBatteryLevel(uint8_t level) {
m_batteryLevelCharacteristic->setValue(&level, 1);
}
/*
* @brief Returns battery level characteristic
* @ return battery level characteristic
*//*
BLECharacteristic* BLEHIDDevice::batteryLevel() {
return m_batteryLevelCharacteristic;
}
BLECharacteristic* BLEHIDDevice::reportMap() {
return m_reportMapCharacteristic;
}
BLECharacteristic* BLEHIDDevice::pnp() {
return m_pnpCharacteristic;
}
BLECharacteristic* BLEHIDDevice::hidInfo() {
return m_hidInfoCharacteristic;
}
*/
/**
* @brief Returns a pointer to the device information service.
*/
NimBLEService* NimBLEHIDDevice::deviceInfo() {
return m_deviceInfoService;
}
/**
* @brief Returns a pointer to the HID service.
*/
NimBLEService* NimBLEHIDDevice::hidService() {
return m_hidService;
}
/**
* @brief @brief Returns a pointer to the battery service.
*/
NimBLEService* NimBLEHIDDevice::batteryService() {
return m_batteryService;
}
#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#endif // #if defined(CONFIG_BT_ENABLED)

89
src/NimBLEHIDDevice.h Normal file
View File

@@ -0,0 +1,89 @@
/*
* NimBLEHIDDevice.h
*
* Created: on Oct 06 2020
* Author wakwak-koba
*
* Originally:
*
* BLEHIDDevice.h
*
* Created on: Jan 03, 2018
* Author: chegewara
*/
#ifndef _BLEHIDDEVICE_H_
#define _BLEHIDDEVICE_H_
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include "nimconfig.h"
#if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
#include "NimBLECharacteristic.h"
#include "NimBLEService.h"
#include "NimBLEDescriptor.h"
#include "HIDTypes.h"
#define GENERIC_HID 0x03C0
#define HID_KEYBOARD 0x03C1
#define HID_MOUSE 0x03C2
#define HID_JOYSTICK 0x03C3
#define HID_GAMEPAD 0x03C4
#define HID_TABLET 0x03C5
#define HID_CARD_READER 0x03C6
#define HID_DIGITAL_PEN 0x03C7
#define HID_BARCODE 0x03C8
/**
* @brief A model of a %BLE Human Interface Device.
*/
class NimBLEHIDDevice {
public:
NimBLEHIDDevice(NimBLEServer*);
virtual ~NimBLEHIDDevice();
void reportMap(uint8_t* map, uint16_t);
void startServices();
NimBLEService* deviceInfo();
NimBLEService* hidService();
NimBLEService* batteryService();
NimBLECharacteristic* manufacturer();
void manufacturer(std::string name);
//NimBLECharacteristic* pnp();
void pnp(uint8_t sig, uint16_t vid, uint16_t pid, uint16_t version);
//NimBLECharacteristic* hidInfo();
void hidInfo(uint8_t country, uint8_t flags);
//NimBLECharacteristic* batteryLevel();
void setBatteryLevel(uint8_t level);
//NimBLECharacteristic* reportMap();
NimBLECharacteristic* hidControl();
NimBLECharacteristic* inputReport(uint8_t reportID);
NimBLECharacteristic* outputReport(uint8_t reportID);
NimBLECharacteristic* featureReport(uint8_t reportID);
NimBLECharacteristic* protocolMode();
NimBLECharacteristic* bootInput();
NimBLECharacteristic* bootOutput();
private:
NimBLEService* m_deviceInfoService; //0x180a
NimBLEService* m_hidService; //0x1812
NimBLEService* m_batteryService = 0; //0x180f
NimBLECharacteristic* m_manufacturerCharacteristic; //0x2a29
NimBLECharacteristic* m_pnpCharacteristic; //0x2a50
NimBLECharacteristic* m_hidInfoCharacteristic; //0x2a4a
NimBLECharacteristic* m_reportMapCharacteristic; //0x2a4b
NimBLECharacteristic* m_hidControlCharacteristic; //0x2a4c
NimBLECharacteristic* m_protocolModeCharacteristic; //0x2a4e
NimBLECharacteristic* m_batteryLevelCharacteristic; //0x2a19
};
#endif // CONFIG_BT_NIMBLE_ROLE_BROADCASTER
#endif // CONFIG_BT_ENABLED
#endif /* _BLEHIDDEVICE_H_ */

View File

@@ -38,7 +38,7 @@ static const char* LOG_TAG = "NimBLERemoteCharacteristic";
NimBLERemoteCharacteristic::NimBLERemoteCharacteristic(NimBLERemoteService *pRemoteService,
const struct ble_gatt_chr *chr)
{
NIMBLE_LOGD(LOG_TAG, ">> NimBLERemoteCharacteristic()");
switch (chr->uuid.u.type) {
case BLE_UUID_TYPE_16:
m_uuid = NimBLEUUID(chr->uuid.u16.value);
@@ -50,7 +50,6 @@ static const char* LOG_TAG = "NimBLERemoteCharacteristic";
m_uuid = NimBLEUUID(const_cast<ble_uuid128_t*>(&chr->uuid.u128));
break;
default:
m_uuid = nullptr;
break;
}
@@ -61,6 +60,8 @@ static const char* LOG_TAG = "NimBLERemoteCharacteristic";
m_notifyCallback = nullptr;
m_timestamp = 0;
m_valMux = portMUX_INITIALIZER_UNLOCKED;
NIMBLE_LOGD(LOG_TAG, "<< NimBLERemoteCharacteristic(): %s", m_uuid.toString().c_str());
} // NimBLERemoteCharacteristic
@@ -208,15 +209,21 @@ int NimBLERemoteCharacteristic::descriptorDiscCB(uint16_t conn_handle,
bool NimBLERemoteCharacteristic::retrieveDescriptors(const NimBLEUUID *uuid_filter) {
NIMBLE_LOGD(LOG_TAG, ">> retrieveDescriptors() for characteristic: %s", getUUID().toString().c_str());
uint16_t endHandle = getRemoteService()->getEndHandle(this);
if(m_handle >= endHandle) {
return false;
}
int rc = 0;
ble_task_data_t taskData = {this, xTaskGetCurrentTaskHandle(), 0, nullptr};
desc_filter_t filter = {uuid_filter, &taskData};
rc = ble_gattc_disc_all_dscs(getRemoteService()->getClient()->getConnId(),
m_handle,
getRemoteService()->getEndHandle(),
endHandle,
NimBLERemoteCharacteristic::descriptorDiscCB,
&filter);
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "ble_gattc_disc_all_chrs: rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc));
return false;
@@ -225,12 +232,13 @@ bool NimBLERemoteCharacteristic::retrieveDescriptors(const NimBLEUUID *uuid_filt
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
if(taskData.rc != 0) {
NIMBLE_LOGE(LOG_TAG, "ble_gattc_disc_all_chrs: startHandle:%d endHandle:%d taskData.rc=%d %s", m_handle, endHandle, taskData.rc, NimBLEUtils::returnCodeToString(0x0100+taskData.rc));
return false;
}
return true;
NIMBLE_LOGD(LOG_TAG, "<< retrieveDescriptors(): Found %d descriptors.", m_descriptorVector.size());
} // getDescriptors
} // retrieveDescriptors
/**
@@ -243,7 +251,7 @@ NimBLERemoteDescriptor* NimBLERemoteCharacteristic::getDescriptor(const NimBLEUU
for(auto &it: m_descriptorVector) {
if(it->getUUID() == uuid) {
NIMBLE_LOGD(LOG_TAG, "<< getDescriptor: found");
NIMBLE_LOGD(LOG_TAG, "<< getDescriptor: found the descriptor with uuid: %s", uuid.toString().c_str());
return it;
}
}
@@ -253,7 +261,18 @@ NimBLERemoteDescriptor* NimBLERemoteCharacteristic::getDescriptor(const NimBLEUU
if(m_descriptorVector.size() > prev_size) {
return m_descriptorVector.back();
}
// If the request was successful but 16/32 bit descriptor not found
// try again with the 128 bit uuid.
if(uuid.bitSize() == BLE_UUID_TYPE_16 ||
uuid.bitSize() == BLE_UUID_TYPE_32)
{
NimBLEUUID uuid128(uuid);
uuid128.to128();
return getDescriptor(uuid128);
}
}
NIMBLE_LOGD(LOG_TAG, "<< getDescriptor: Not found");
return nullptr;
} // getDescriptor
@@ -447,9 +466,10 @@ std::string NimBLERemoteCharacteristic::readValue(time_t *timestamp) {
}
} while(rc != 0 && retryCount--);
time_t t = time(nullptr);
portENTER_CRITICAL(&m_valMux);
m_value = value;
m_timestamp = time(nullptr);
m_timestamp = t;
if(timestamp != nullptr) {
*timestamp = m_timestamp;
}
@@ -506,19 +526,19 @@ int NimBLERemoteCharacteristic::onReadCB(uint16_t conn_handle,
* @param [in] notifyCallback A callback to be invoked for a notification.
* @param [in] response If write response required set this to true.
* If NULL is provided then no callback is performed.
* @return true if successful.
* @return false if writing to the descriptor failed.
*/
bool NimBLERemoteCharacteristic::setNotify(uint16_t val, notify_callback notifyCallback, bool response) {
NIMBLE_LOGD(LOG_TAG, ">> setNotify(): %s, %02x", toString().c_str(), val);
m_notifyCallback = notifyCallback;
NimBLERemoteDescriptor* desc = getDescriptor(NimBLEUUID((uint16_t)0x2902));
if(desc == nullptr) {
NIMBLE_LOGE(LOG_TAG, "<< setNotify(): Could not get descriptor");
return false;
NIMBLE_LOGW(LOG_TAG, "<< setNotify(): Callback set, CCCD not found");
return true;
}
m_notifyCallback = notifyCallback;
NIMBLE_LOGD(LOG_TAG, "<< setNotify()");
return desc->writeValue((uint8_t *)&val, 2, response);
@@ -531,7 +551,7 @@ bool NimBLERemoteCharacteristic::setNotify(uint16_t val, notify_callback notifyC
* @param [in] notifyCallback A callback to be invoked for a notification.
* @param [in] response If true, require a write response from the descriptor write operation.
* If NULL is provided then no callback is performed.
* @return true if successful.
* @return false if writing to the descriptor failed.
*/
bool NimBLERemoteCharacteristic::subscribe(bool notifications, notify_callback notifyCallback, bool response) {
if(notifications) {
@@ -545,7 +565,7 @@ bool NimBLERemoteCharacteristic::subscribe(bool notifications, notify_callback n
/**
* @brief Unsubscribe for notifications or indications.
* @param [in] response bool if true, require a write response from the descriptor write operation.
* @return true if successful.
* @return false if writing to the descriptor failed.
*/
bool NimBLERemoteCharacteristic::unsubscribe(bool response) {
return setNotify(0x00, nullptr, response);

View File

@@ -31,6 +31,7 @@ static const char* LOG_TAG = "NimBLERemoteDescriptor";
NimBLERemoteDescriptor::NimBLERemoteDescriptor(NimBLERemoteCharacteristic* pRemoteCharacteristic,
const struct ble_gatt_dsc *dsc)
{
NIMBLE_LOGD(LOG_TAG, ">> NimBLERemoteDescriptor()");
switch (dsc->uuid.u.type) {
case BLE_UUID_TYPE_16:
m_uuid = NimBLEUUID(dsc->uuid.u16.value);
@@ -42,12 +43,13 @@ NimBLERemoteDescriptor::NimBLERemoteDescriptor(NimBLERemoteCharacteristic* pRemo
m_uuid = NimBLEUUID(const_cast<ble_uuid128_t*>(&dsc->uuid.u128));
break;
default:
m_uuid = nullptr;
break;
}
m_handle = dsc->handle;
m_pRemoteCharacteristic = pRemoteCharacteristic;
NIMBLE_LOGD(LOG_TAG, "<< NimBLERemoteDescriptor(): %s", m_uuid.toString().c_str());
}

View File

@@ -44,12 +44,11 @@ NimBLERemoteService::NimBLERemoteService(NimBLEClient* pClient, const struct ble
m_uuid = NimBLEUUID(const_cast<ble_uuid128_t*>(&service->uuid.u128));
break;
default:
m_uuid = nullptr;
break;
}
m_startHandle = service->start_handle;
m_endHandle = service->end_handle;
NIMBLE_LOGD(LOG_TAG, "<< NimBLERemoteService()");
NIMBLE_LOGD(LOG_TAG, "<< NimBLERemoteService(): %s", m_uuid.toString().c_str());
}
@@ -95,8 +94,11 @@ NimBLERemoteCharacteristic* NimBLERemoteService::getCharacteristic(const char* u
* @return A pointer to the characteristic object, or nullptr if not found.
*/
NimBLERemoteCharacteristic* NimBLERemoteService::getCharacteristic(const NimBLEUUID &uuid) {
NIMBLE_LOGD(LOG_TAG, ">> getCharacteristic: uuid: %s", uuid.toString().c_str());
for(auto &it: m_characteristicVector) {
if(it->getUUID() == uuid) {
NIMBLE_LOGD(LOG_TAG, "<< getCharacteristic: found the characteristic with uuid: %s", uuid.toString().c_str());
return it;
}
}
@@ -106,8 +108,19 @@ NimBLERemoteCharacteristic* NimBLERemoteService::getCharacteristic(const NimBLEU
if(m_characteristicVector.size() > prev_size) {
return m_characteristicVector.back();
}
// If the request was successful but 16/32 bit characteristic not found
// try again with the 128 bit uuid.
if(uuid.bitSize() == BLE_UUID_TYPE_16 ||
uuid.bitSize() == BLE_UUID_TYPE_32)
{
NimBLEUUID uuid128(uuid);
uuid128.to128();
return getCharacteristic(uuid128);
}
}
NIMBLE_LOGD(LOG_TAG, "<< getCharacteristic: not found");
return nullptr;
} // getCharacteristic
@@ -236,6 +249,23 @@ uint16_t NimBLERemoteService::getEndHandle() {
return m_endHandle;
} // getEndHandle
/**
* @brief Get the end handle of specified NimBLERemoteCharacteristic.
*/
uint16_t NimBLERemoteService::getEndHandle(NimBLERemoteCharacteristic *pCharacteristic) {
uint16_t endHandle = m_endHandle;
for(auto &it: m_characteristicVector) {
uint16_t defHandle = it->getDefHandle() - 1;
if(defHandle > pCharacteristic->getDefHandle() && endHandle > defHandle) {
endHandle = defHandle;
}
}
return endHandle;
} // getEndHandle
/**
* @brief Get the service start handle.

View File

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

View File

@@ -30,7 +30,6 @@ static const char* LOG_TAG = "NimBLEScan";
* @brief Scan constuctor.
*/
NimBLEScan::NimBLEScan() {
m_own_addr_type = 0;
m_scan_params.filter_policy = BLE_HCI_SCAN_FILT_NO_WL;
m_scan_params.passive = 1; // If set, dont send scan requests to advertisers (i.e., dont request additional advertising data).
m_scan_params.itvl = 0; // This is defined as the time interval from when the Controller started its last LE scan until it begins the subsequent LE scan. (units=0.625 msec)
@@ -38,9 +37,10 @@ NimBLEScan::NimBLEScan() {
m_scan_params.limited = 0; // If set, only discover devices in limited discoverable mode.
m_scan_params.filter_duplicates = 0; // If set, the controller ignores all but the first advertisement from each device.
m_pAdvertisedDeviceCallbacks = nullptr;
m_stopped = true;
m_wantDuplicates = false;
m_ignoreResults = false;
m_pTaskData = nullptr;
m_duration = BLE_HS_FOREVER; // make sure this is non-zero in the event of a host reset
m_maxResults = 0xFF;
}
@@ -63,8 +63,8 @@ NimBLEScan::~NimBLEScan() {
switch(event->type) {
case BLE_GAP_EVENT_DISC: {
if(pScan->m_stopped) {
NIMBLE_LOGE(LOG_TAG, "Scan stop called, ignoring results.");
if(pScan->m_ignoreResults) {
NIMBLE_LOGI(LOG_TAG, "Scan op in progress - ignoring results");
return 0;
}
@@ -88,34 +88,49 @@ NimBLEScan::~NimBLEScan() {
// If we haven't seen this device before; create a new instance and insert it in the vector.
// Otherwise just update the relevant parameters of the already known device.
if(advertisedDevice == nullptr){
if(advertisedDevice == nullptr && event->disc.event_type != BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP){
// Check if we have reach the scan results limit, ignore this one if so.
// We still need to store each device when maxResults is 0 to be able to append the scan results
if(pScan->m_maxResults > 0 && pScan->m_maxResults < 0xFF &&
(pScan->m_scanResults.m_advertisedDevicesVector.size() >= pScan->m_maxResults))
{
return 0;
}
advertisedDevice = new NimBLEAdvertisedDevice();
advertisedDevice->setAddress(advertisedAddress);
advertisedDevice->setAdvType(event->disc.event_type);
pScan->m_scanResults.m_advertisedDevicesVector.push_back(advertisedDevice);
NIMBLE_LOGI(LOG_TAG, "NEW DEVICE FOUND: %s", advertisedAddress.toString().c_str());
}
else{
NIMBLE_LOGI(LOG_TAG, "UPDATING PREVIOUSLY FOUND DEVICE: %s", advertisedAddress.toString().c_str());
}
advertisedDevice->setRSSI(event->disc.rssi);
if(event->disc.length_data > 0) {
advertisedDevice->parseAdvertisement(event->disc.data, event->disc.length_data);
NIMBLE_LOGI(LOG_TAG, "New advertiser: %s", advertisedAddress.toString().c_str());
} else if(advertisedDevice != nullptr) {
NIMBLE_LOGI(LOG_TAG, "Updated advertiser: %s", advertisedAddress.toString().c_str());
} else {
// Scan response from unknown device
return 0;
}
advertisedDevice->m_timestamp = time(nullptr);
advertisedDevice->setRSSI(event->disc.rssi);
advertisedDevice->setPayload(event->disc.data, event->disc.length_data,
event->disc.event_type == BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP);
if (pScan->m_pAdvertisedDeviceCallbacks) {
if(pScan->m_wantDuplicates || !advertisedDevice->m_callbackSent) {
// If not active scanning report the result to the listener.
if(pScan->m_scan_params.passive || event->disc.event_type == BLE_HCI_ADV_TYPE_ADV_NONCONN_IND) {
advertisedDevice->m_callbackSent = true;
pScan->m_pAdvertisedDeviceCallbacks->onResult(advertisedDevice);
// If not active scanning or scan response is not available
// report the result to the callback now.
if(pScan->m_scan_params.passive ||
(advertisedDevice->getAdvType() != BLE_HCI_ADV_TYPE_ADV_IND &&
advertisedDevice->getAdvType() != BLE_HCI_ADV_TYPE_ADV_SCAN_IND))
{
advertisedDevice->m_callbackSent = true;
pScan->m_pAdvertisedDeviceCallbacks->onResult(advertisedDevice);
// Otherwise wait for the scan response so we can report all of the data at once.
} else if (event->disc.event_type == BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP) {
advertisedDevice->m_callbackSent = true;
pScan->m_pAdvertisedDeviceCallbacks->onResult(advertisedDevice);
}
// Otherwise, wait for the scan response so we can report the complete data.
} else if (event->disc.event_type == BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP) {
advertisedDevice->m_callbackSent = true;
pScan->m_pAdvertisedDeviceCallbacks->onResult(advertisedDevice);
}
// If not storing results and we have invoked the callback, delete the device.
if(pScan->m_maxResults == 0 && advertisedDevice->m_callbackSent) {
pScan->erase(advertisedAddress);
}
}
@@ -123,13 +138,26 @@ NimBLEScan::~NimBLEScan() {
}
case BLE_GAP_EVENT_DISC_COMPLETE: {
NIMBLE_LOGD(LOG_TAG, "discovery complete; reason=%d",
event->disc_complete.reason);
event->disc_complete.reason);
// If a device advertised with scan reponse available and it was not received
// the callback would not have been invoked, so do it here.
if(pScan->m_pAdvertisedDeviceCallbacks) {
for(auto &it : pScan->m_scanResults.m_advertisedDevicesVector) {
if(!it->m_callbackSent) {
pScan->m_pAdvertisedDeviceCallbacks->onResult(it);
}
}
}
if(pScan->m_maxResults == 0) {
pScan->clearResults();
}
if (pScan->m_scanCompleteCB != nullptr) {
pScan->m_scanCompleteCB(pScan->m_scanResults);
}
pScan->m_stopped = true;
if(pScan->m_pTaskData != nullptr) {
pScan->m_pTaskData->rc = event->disc_complete.reason;
xTaskNotifyGive(pScan->m_pTaskData->task);
@@ -146,15 +174,11 @@ NimBLEScan::~NimBLEScan() {
/**
* @brief Should we perform an active or passive scan?
* The default is a passive scan. An active scan means that we will wish a scan response.
* The default is a passive scan. An active scan means that we will request a scan response.
* @param [in] active If true, we perform an active scan otherwise a passive scan.
*/
void NimBLEScan::setActiveScan(bool active) {
if (active) {
m_scan_params.passive = 0;
} else {
m_scan_params.passive = 1;
}
m_scan_params.passive = !active;
} // setActiveScan
@@ -203,6 +227,16 @@ void NimBLEScan::setFilterPolicy(uint8_t filter) {
} // setFilterPolicy
/**
* @brief Sets the max number of results to store.
* @param [in] maxResults The number of results to limit storage to\n
* 0 == none (callbacks only) 0xFF == unlimited, any other value is the limit.
*/
void NimBLEScan::setMaxResults(uint8_t maxResults) {
m_maxResults = maxResults;
}
/**
* @brief Set the call backs to be invoked.
* @param [in] pAdvertisedDeviceCallbacks Call backs to be invoked.
@@ -210,7 +244,7 @@ void NimBLEScan::setFilterPolicy(uint8_t filter) {
*/
void NimBLEScan::setAdvertisedDeviceCallbacks(NimBLEAdvertisedDeviceCallbacks* pAdvertisedDeviceCallbacks,
bool wantDuplicates) {
m_wantDuplicates = wantDuplicates;
setDuplicateFilter(!wantDuplicates);
m_pAdvertisedDeviceCallbacks = pAdvertisedDeviceCallbacks;
} // setAdvertisedDeviceCallbacks
@@ -238,7 +272,7 @@ void NimBLEScan::setWindow(uint16_t windowMSecs) {
* @return true if scanning or scan starting.
*/
bool NimBLEScan::isScanning() {
return !m_stopped;
return ble_gap_disc_active();
}
@@ -252,25 +286,6 @@ bool NimBLEScan::isScanning() {
bool NimBLEScan::start(uint32_t duration, void (*scanCompleteCB)(NimBLEScanResults), bool is_continue) {
NIMBLE_LOGD(LOG_TAG, ">> start(duration=%d)", duration);
// If Host is not synced we cannot start scanning.
if(!NimBLEDevice::m_synced) {
NIMBLE_LOGC(LOG_TAG, "Host reset, wait for sync.");
return false;
}
if(ble_gap_conn_active()) {
NIMBLE_LOGE(LOG_TAG, "Connection in progress - must wait.");
return false;
}
// If we are already scanning don't start again or we will get stuck on the semaphore.
if(!m_stopped || ble_gap_disc_active()) { // double check - can cause host reset.
NIMBLE_LOGE(LOG_TAG, "Scan already in progress");
return false;
}
m_stopped = false;
// Save the callback to be invoked when the scan completes.
m_scanCompleteCB = scanCompleteCB;
// Save the duration in the case that the host is reset so we can reuse it.
@@ -281,32 +296,51 @@ bool NimBLEScan::start(uint32_t duration, void (*scanCompleteCB)(NimBLEScanResul
duration = BLE_HS_FOREVER;
}
else{
duration = duration*1000; // convert duration to milliseconds
// convert duration to milliseconds
duration = duration * 1000;
}
// if we are connecting to devices that are advertising even after being connected, multiconnecting peripherals
// then we should not clear vector or we will connect the same device few times
// Set the flag to ignore the results while we are deleting the vector
if(!is_continue) {
clearResults();
m_ignoreResults = true;
}
int rc = 0;
do{
rc = ble_gap_disc(m_own_addr_type, duration, &m_scan_params,
NimBLEScan::handleGapEvent, this);
if(rc == BLE_HS_EBUSY) {
vTaskDelay(1 / portTICK_PERIOD_MS);
}
} while(rc == BLE_HS_EBUSY);
int rc = ble_gap_disc(NimBLEDevice::m_own_addr_type, duration, &m_scan_params,
NimBLEScan::handleGapEvent, this);
if (rc != 0 && rc != BLE_HS_EDONE) {
NIMBLE_LOGE(LOG_TAG, "Error initiating GAP discovery procedure; rc=%d, %s",
rc, NimBLEUtils::returnCodeToString(rc));
m_stopped = true;
switch(rc) {
case 0:
if(!is_continue) {
clearResults();
}
break;
case BLE_HS_EALREADY:
break;
case BLE_HS_EBUSY:
NIMBLE_LOGE(LOG_TAG, "Unable to scan - connection in progress.");
break;
case BLE_HS_ETIMEOUT_HCI:
case BLE_HS_EOS:
case BLE_HS_ECONTROLLER:
case BLE_HS_ENOTSYNCED:
NIMBLE_LOGC(LOG_TAG, "Unable to scan - Host Reset");
break;
default:
NIMBLE_LOGE(LOG_TAG, "Error initiating GAP discovery procedure; rc=%d, %s",
rc, NimBLEUtils::returnCodeToString(rc));
break;
}
m_ignoreResults = false;
NIMBLE_LOGD(LOG_TAG, "<< start()");
if(rc != 0 && rc != BLE_HS_EALREADY) {
return false;
}
NIMBLE_LOGD(LOG_TAG, "<< start()");
return true;
} // start
@@ -343,11 +377,13 @@ bool NimBLEScan::stop() {
int rc = ble_gap_disc_cancel();
if (rc != 0 && rc != BLE_HS_EALREADY) {
NIMBLE_LOGE(LOG_TAG, "Failed to cancel scan; rc=%d\n", rc);
NIMBLE_LOGE(LOG_TAG, "Failed to cancel scan; rc=%d", rc);
return false;
}
m_stopped = true;
if(m_maxResults == 0) {
clearResults();
}
if (rc != BLE_HS_EALREADY && m_scanCompleteCB != nullptr) {
m_scanCompleteCB(m_scanResults);
@@ -368,7 +404,7 @@ bool NimBLEScan::stop() {
* @details After disconnecting, it may be required in the case we were connected to a device without a public address.
*/
void NimBLEScan::erase(const NimBLEAddress &address) {
NIMBLE_LOGI(LOG_TAG, "erase device: %s", address.toString().c_str());
NIMBLE_LOGD(LOG_TAG, "erase device: %s", address.toString().c_str());
for(auto it = m_scanResults.m_advertisedDevicesVector.begin(); it != m_scanResults.m_advertisedDevicesVector.end(); ++it) {
if((*it)->getAddress() == address) {
@@ -381,13 +417,25 @@ void NimBLEScan::erase(const NimBLEAddress &address) {
/**
* @brief If the host reset the scan will have stopped so we should set the flag as stopped.
* @brief Called when host reset, we set a flag to stop scanning until synced.
*/
void NimBLEScan::onHostReset() {
m_stopped = true;
m_ignoreResults = true;
}
/**
* @brief If the host reset and re-synced this is called.
* If the application was scanning indefinitely with a callback, restart it.
*/
void NimBLEScan::onHostSync() {
m_ignoreResults = false;
if(m_duration == 0 && m_pAdvertisedDeviceCallbacks != nullptr) {
start(m_duration, m_scanCompleteCB);
}
}
/**
* @brief Get the results of the scan.
* @return NimBLEScanResults object.

View File

@@ -73,6 +73,7 @@ public:
bool stop();
void clearResults();
NimBLEScanResults getResults();
void setMaxResults(uint8_t maxResults);
void erase(const NimBLEAddress &address);
@@ -83,16 +84,16 @@ private:
~NimBLEScan();
static int handleGapEvent(ble_gap_event* event, void* arg);
void onHostReset();
void onHostSync();
NimBLEAdvertisedDeviceCallbacks* m_pAdvertisedDeviceCallbacks = nullptr;
void (*m_scanCompleteCB)(NimBLEScanResults scanResults);
ble_gap_disc_params m_scan_params;
uint8_t m_own_addr_type;
bool m_stopped;
bool m_wantDuplicates;
bool m_ignoreResults;
NimBLEScanResults m_scanResults;
uint32_t m_duration;
ble_task_data_t *m_pTaskData;
uint8_t m_maxResults;
};
#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)

View File

@@ -104,28 +104,47 @@ NimBLEService* NimBLEServer::createService(const NimBLEUUID &uuid, uint32_t numH
/**
* @brief Get a %BLE Service by its UUID
* @param [in] uuid The UUID of the new service.
* @return A reference to the service object.
* @param [in] uuid The UUID of the service.
* @param instanceId The index of the service to return (used when multiple services have the same UUID).
* @return A pointer to the service object or nullptr if not found.
*/
NimBLEService* NimBLEServer::getServiceByUUID(const char* uuid) {
return getServiceByUUID(NimBLEUUID(uuid));
NimBLEService* NimBLEServer::getServiceByUUID(const char* uuid, uint16_t instanceId) {
return getServiceByUUID(NimBLEUUID(uuid), instanceId);
} // getServiceByUUID
/**
* @brief Get a %BLE Service by its UUID
* @param [in] uuid The UUID of the new service.
* @return A reference to the service object.
* @param [in] uuid The UUID of the service.
* @param instanceId The index of the service to return (used when multiple services have the same UUID).
* @return A pointer to the service object or nullptr if not found.
*/
NimBLEService* NimBLEServer::getServiceByUUID(const NimBLEUUID &uuid) {
NimBLEService* NimBLEServer::getServiceByUUID(const NimBLEUUID &uuid, uint16_t instanceId) {
uint16_t position = 0;
for (auto &it : m_svcVec) {
if (it->getUUID() == uuid) {
return it;
if (position == instanceId){
return it;
}
position++;
}
}
return nullptr;
} // getServiceByUUID
/**
* @brief Get a %BLE Service by its handle
* @param handle The handle of the service.
* @return A pointer to the service object or nullptr if not found.
*/
NimBLEService *NimBLEServer::getServiceByHandle(uint16_t handle) {
for (auto &it : m_svcVec) {
if (it->getHandle() == handle) {
return it;
}
}
return nullptr;
}
/**
* @brief Retrieve the advertising object that can be used to advertise the existence of the server.
@@ -296,6 +315,7 @@ size_t NimBLEServer::getConnectedCount() {
}
server->m_pServerCallbacks->onDisconnect(server);
server->m_pServerCallbacks->onDisconnect(server, &event->disconnect.conn);
if(server->m_advertiseOnDisconnect) {
server->startAdvertising();
@@ -620,7 +640,13 @@ uint16_t NimBLEServer::getPeerMTU(uint16_t conn_id) {
/**
* Update connection parameters can be called only after connection has been established
* @brief Request an Update the connection parameters:
* * Can only be used after a connection has been established.
* @param [in] conn_handle The connection handle of the peer to send the request to.
* @param [in] minInterval The minimum connection interval in 1.25ms units.
* @param [in] maxInterval The maximum connection interval in 1.25ms units.
* @param [in] latency The number of packets allowed to skip (extends max interval).
* @param [in] timeout The timeout time in 10ms units before disconnecting.
*/
void NimBLEServer::updateConnParams(uint16_t conn_handle,
uint16_t minInterval, uint16_t maxInterval,
@@ -639,7 +665,7 @@ void NimBLEServer::updateConnParams(uint16_t conn_handle,
if(rc != 0) {
NIMBLE_LOGE(LOG_TAG, "Update params error: %d, %s", rc, NimBLEUtils::returnCodeToString(rc));
}
} // updateConnParams
}// updateConnParams
/** Default callback handlers */
@@ -658,6 +684,10 @@ void NimBLEServerCallbacks::onDisconnect(NimBLEServer* pServer) {
NIMBLE_LOGD("NimBLEServerCallbacks", "onDisconnect(): Default");
} // onDisconnect
void NimBLEServerCallbacks::onDisconnect(NimBLEServer* pServer, ble_gap_conn_desc* desc) {
NIMBLE_LOGD("NimBLEServerCallbacks", "onDisconnect(): Default");
} // onDisconnect
uint32_t NimBLEServerCallbacks::onPassKeyRequest(){
NIMBLE_LOGD("NimBLEServerCallbacks", "onPassKeyRequest: default: 123456");
return 123456;

View File

@@ -49,8 +49,9 @@ public:
void startAdvertising();
void stopAdvertising();
void start();
NimBLEService* getServiceByUUID(const char* uuid);
NimBLEService* getServiceByUUID(const NimBLEUUID &uuid);
NimBLEService* getServiceByUUID(const char* uuid, uint16_t instanceId = 0);
NimBLEService* getServiceByUUID(const NimBLEUUID &uuid, uint16_t instanceId = 0);
NimBLEService* getServiceByHandle(uint16_t handle);
int disconnect(uint16_t connID,
uint8_t reason = BLE_ERR_REM_USER_CONN_TERM);
void updateConnParams(uint16_t conn_handle,
@@ -114,6 +115,15 @@ public:
*/
virtual void onDisconnect(NimBLEServer* pServer);
/**
* @brief Handle a client disconnection.
* This is called when a client discconnects.
* @param [in] pServer A pointer to the %BLE server that received the client disconnection.
* @param [in] desc A pointer to the connection description structure containig information
* about the connection.
*/
virtual void onDisconnect(NimBLEServer* pServer, ble_gap_conn_desc* desc);
/**
* @brief Called when a client requests a passkey for pairing.
* @return The passkey to be sent to the client.

View File

@@ -233,9 +233,9 @@ NimBLECharacteristic* NimBLEService::createCharacteristic(const char* uuid, uint
*/
NimBLECharacteristic* NimBLEService::createCharacteristic(const NimBLEUUID &uuid, uint32_t properties) {
NimBLECharacteristic* pCharacteristic = new NimBLECharacteristic(uuid, properties, this);
// Check that we don't add the same characteristic twice.
if (getCharacteristic(uuid) != nullptr) {
NIMBLE_LOGW(LOG_TAG, "<< Adding a duplicate characteristic with UUID: %s",
NIMBLE_LOGD(LOG_TAG, "<< Adding a duplicate characteristic with UUID: %s",
std::string(uuid).c_str());
}
@@ -249,28 +249,72 @@ NimBLECharacteristic* NimBLEService::createCharacteristic(const NimBLEUUID &uuid
/**
* @brief Get a pointer to the characteristic object with the specified UUID.
* @param [in] uuid The UUID of the characteristic.
* @param instanceId The index of the characteristic to return (used when multiple characteristics have the same UUID).
* @return A pointer to the characteristic object or nullptr if not found.
*/
NimBLECharacteristic* NimBLEService::getCharacteristic(const char* uuid) {
return getCharacteristic(NimBLEUUID(uuid));
NimBLECharacteristic* NimBLEService::getCharacteristic(const char* uuid, uint16_t instanceId) {
return getCharacteristic(NimBLEUUID(uuid), instanceId);
}
/**
* @brief Get a pointer to the characteristic object with the specified UUID.
* @param [in] uuid The UUID of the characteristic.
* @param instanceId The index of the characteristic to return (used when multiple characteristics have the same UUID).
* @return A pointer to the characteristic object or nullptr if not found.
*/
NimBLECharacteristic* NimBLEService::getCharacteristic(const NimBLEUUID &uuid) {
NimBLECharacteristic* NimBLEService::getCharacteristic(const NimBLEUUID &uuid, uint16_t instanceId) {
uint16_t position = 0;
for (auto &it : m_chrVec) {
if (it->getUUID() == uuid) {
return it;
if (position == instanceId) {
return it;
}
position++;
}
}
return nullptr;
}
/**
* @brief Get a pointer to the characteristic object with the specified handle.
* @param handle The handle of the characteristic.
* @return A pointer to the characteristic object or nullptr if not found.
*/
NimBLECharacteristic *NimBLEService::getCharacteristicByHandle(uint16_t handle) {
for (auto &it : m_chrVec) {
if (it->getHandle() == handle) {
return it;
}
}
return nullptr;
}
/**
* @return A vector containing pointers to each characteristic associated with this service.
*/
std::vector<NimBLECharacteristic *> NimBLEService::getCharacteristics() {
return m_chrVec;
}
/**
* @return A vector containing pointers to each characteristic with the provided UUID associated with this service.
*/
std::vector<NimBLECharacteristic *> NimBLEService::getCharacteristics(const char *uuid) {
return getCharacteristics(NimBLEUUID(uuid));
}
/**
* @return A vector containing pointers to each characteristic with the provided UUID associated with this service.
*/
std::vector<NimBLECharacteristic *> NimBLEService::getCharacteristics(const NimBLEUUID &uuid) {
std::vector<NimBLECharacteristic*> result;
for (auto &it : m_chrVec) {
if (it->getUUID() == uuid) {
result.push_back(it);
}
}
return result;
}
/**
* @brief Return a string representation of this service.
@@ -295,7 +339,7 @@ std::string NimBLEService::toString() {
*/
NimBLEServer* NimBLEService::getServer() {
return m_pServer;
} // getServer
}// getServer
#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#endif // CONFIG_BT_ENABLED

View File

@@ -35,6 +35,16 @@ class NimBLECharacteristic;
*/
class NimBLEService {
public:
NimBLEServer* getServer();
NimBLEUUID getUUID();
uint16_t getHandle();
std::string toString();
void dump();
bool start();
NimBLECharacteristic* createCharacteristic(const char* uuid,
uint32_t properties =
NIMBLE_PROPERTY::READ |
@@ -45,14 +55,14 @@ public:
NIMBLE_PROPERTY::READ |
NIMBLE_PROPERTY::WRITE);
void dump();
NimBLECharacteristic* getCharacteristic(const char* uuid);
NimBLECharacteristic* getCharacteristic(const NimBLEUUID &uuid);
NimBLEUUID getUUID();
NimBLEServer* getServer();
bool start();
std::string toString();
uint16_t getHandle();
NimBLECharacteristic* getCharacteristic(const char* uuid, uint16_t instanceId = 0);
NimBLECharacteristic* getCharacteristic(const NimBLEUUID &uuid, uint16_t instanceId = 0);
NimBLECharacteristic* getCharacteristicByHandle(uint16_t handle);
std::vector<NimBLECharacteristic*> getCharacteristics();
std::vector<NimBLECharacteristic*> getCharacteristics(const char* uuid);
std::vector<NimBLECharacteristic*> getCharacteristics(const NimBLEUUID &uuid);
private:
NimBLEService(const char* uuid, uint16_t numHandles, NimBLEServer* pServer);

View File

@@ -65,29 +65,42 @@ static const char* LOG_TAG = "NimBLEUUID";
*this = NimBLEUUID(first, second, third, (uint64_t(fourth) << 48) + fifth);
}
else {
NIMBLE_LOGE(LOG_TAG,"ERROR: UUID value not 2, 4, 16 or 36 bytes");
m_valueSet = false;
}
} // NimBLEUUID(std::string)
/**
* @brief Create a UUID from 16 bytes of memory.
* @brief Create a UUID from 2, 4, 16 bytes of memory.
* @param [in] pData The pointer to the start of the UUID.
* @param [in] size The size of the data.
* @param [in] msbFirst Is the MSB first in pData memory?
*/
NimBLEUUID::NimBLEUUID(const uint8_t* pData, size_t size, bool msbFirst) {
if (size != 16) {
NIMBLE_LOGE(LOG_TAG,"ERROR: UUID length not 16 bytes");
return;
}
m_uuid.u.type = BLE_UUID_TYPE_128;
uint8_t *uuidValue = nullptr;
switch(size) {
case 2:
uuidValue = (uint8_t*)&m_uuid.u16.value;
m_uuid.u.type = BLE_UUID_TYPE_16;
break;
case 4:
uuidValue = (uint8_t*)&m_uuid.u32.value;
m_uuid.u.type = BLE_UUID_TYPE_32;
break;
case 16:
uuidValue = m_uuid.u128.value;
m_uuid.u.type = BLE_UUID_TYPE_128;
break;
default:
m_valueSet = false;
NIMBLE_LOGE(LOG_TAG, "Invalid UUID size");
return;
}
if (msbFirst) {
std::reverse_copy(pData, pData + 16, m_uuid.u128.value);
std::reverse_copy(pData, pData + size, uuidValue);
} else {
memcpy(m_uuid.u128.value, pData, 16);
memcpy(uuidValue, pData, size);
}
m_valueSet = true;
} // NimBLEUUID
@@ -264,6 +277,37 @@ std::string NimBLEUUID::toString() const {
*/
bool NimBLEUUID::operator ==(const NimBLEUUID & rhs) const {
if(m_valueSet && rhs.m_valueSet) {
NIMBLE_LOGD(LOG_TAG,"Comparing UUIDs; type %u to %u; UUID %s to %s",
m_uuid.u.type, rhs.m_uuid.u.type,
this->toString().c_str(), rhs.toString().c_str());
if(m_uuid.u.type != rhs.m_uuid.u.type) {
uint8_t uuidBase[16] = {
0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80,
0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
if(m_uuid.u.type == BLE_UUID_TYPE_128){
if(rhs.m_uuid.u.type == BLE_UUID_TYPE_16){
memcpy(uuidBase+12, &rhs.m_uuid.u16.value, 2);
} else if (rhs.m_uuid.u.type == BLE_UUID_TYPE_32){
memcpy(uuidBase+12, &rhs.m_uuid.u32.value, 4);
}
return memcmp(m_uuid.u128.value,uuidBase,16) == 0;
} else if(rhs.m_uuid.u.type == BLE_UUID_TYPE_128) {
if(m_uuid.u.type == BLE_UUID_TYPE_16){
memcpy(uuidBase+12, &m_uuid.u16.value, 2);
} else if (m_uuid.u.type == BLE_UUID_TYPE_32){
memcpy(uuidBase+12, &m_uuid.u32.value, 4);
}
return memcmp(rhs.m_uuid.u128.value,uuidBase,16) == 0;
} else {
return false;
}
}
return ble_uuid_cmp(&m_uuid.u, &rhs.m_uuid.u) == 0;
}

View File

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

53
src/nimconfig_rename.h Normal file
View File

@@ -0,0 +1,53 @@
/*
* For ESP-IDF compatibility
* Some versions of ESP-IDF used the config name format "CONFIG_NIMBLE_".
* This converts them to "CONFIG_BT_NIMBLE_" format used in the latest IDF.
*/
#if defined(CONFIG_NIMBLE_ENABLED) && !defined(CONFIG_BT_NIMBLE_ENABLED)
#define CONFIG_BT_NIMBLE_ENABLED
#endif
#if defined(CONFIG_NIMBLE_ROLE_OBSERVER) && !defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
#define CONFIG_BT_NIMBLE_ROLE_OBSERVER
#endif
#if defined(CONFIG_NIMBLE_ROLE_BROADCASTER) && !defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
#define CONFIG_BT_NIMBLE_ROLE_BROADCASTER
#endif
#if defined(CONFIG_NIMBLE_ROLE_CENTRAL) && !defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
#define CONFIG_BT_NIMBLE_ROLE_CENTRAL
#endif
#if defined(CONFIG_NIMBLE_ROLE_PERIPHERAL) && !defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#define CONFIG_BT_NIMBLE_ROLE_PERIPHERAL
#endif
#if defined(CONFIG_NIMBLE_DEBUG) && !defined(CONFIG_BT_NIMBLE_DEBUG)
#define CONFIG_BT_NIMBLE_DEBUG
#endif
#if defined(CONFIG_SCAN_DUPLICATE_BY_DEVICE_ADDR) && !defined(CONFIG_BTDM_SCAN_DUPL_TYPE_DEVICE)
#define CONFIG_BTDM_SCAN_DUPL_TYPE_DEVICE CONFIG_SCAN_DUPLICATE_BY_DEVICE_ADDR
#endif
#if defined(CONFIG_SCAN_DUPLICATE_BY_ADV_DATA ) && !defined(CONFIG_BTDM_SCAN_DUPL_TYPE_DATA)
#define CONFIG_BTDM_SCAN_DUPL_TYPE_DATA CONFIG_SCAN_DUPLICATE_BY_ADV_DATA
#endif
#if defined(CONFIG_SCAN_DUPLICATE_BY_ADV_DATA_AND_DEVICE_ADDR) && !defined(CONFIG_BTDM_SCAN_DUPL_TYPE_DATA_DEVICE)
#define CONFIG_BTDM_SCAN_DUPL_TYPE_DATA_DEVICE CONFIG_SCAN_DUPLICATE_BY_ADV_DATA_AND_DEVICE_ADDR
#endif
#if defined(CONFIG_SCAN_DUPLICATE_TYPE) && !defined(CONFIG_BTDM_SCAN_DUPL_TYPE)
#define CONFIG_BTDM_SCAN_DUPL_TYPE CONFIG_SCAN_DUPLICATE_TYPE
#endif
#if defined(CONFIG_DUPLICATE_SCAN_CACHE_SIZE) && !defined(CONFIG_BTDM_SCAN_DUPL_CACHE_SIZE)
#define CONFIG_BTDM_SCAN_DUPL_CACHE_SIZE CONFIG_DUPLICATE_SCAN_CACHE_SIZE
#endif
#if defined(CONFIG_NIMBLE_MAX_CONNECTIONS ) && !defined(CONFIG_BT_NIMBLE_MAX_CONNECTIONS)
#define CONFIG_BT_NIMBLE_MAX_CONNECTIONS CONFIG_NIMBLE_MAX_CONNECTIONS
#endif