BLE updates for esp-nimble-cpp v2.x (#23553)

This commit is contained in:
Christian Baars 2025-06-15 18:24:54 +02:00 committed by GitHub
parent f378e68b3d
commit fb44d42426
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
158 changed files with 12210 additions and 11639 deletions

View File

@ -1,6 +1,6 @@
/******************************************************************** /********************************************************************
* Tasmota lib * Tasmota lib
* *
* To use: `import MI32` * To use: `import MI32`
*******************************************************************/ *******************************************************************/
#include "be_constobj.h" #include "be_constobj.h"
@ -44,7 +44,7 @@ module MI32 (scope: global) {
/******************************************************************** /********************************************************************
* Tasmota lib * Tasmota lib
* *
* To use: `import BLE` * To use: `import BLE`
*******************************************************************/ *******************************************************************/
@ -73,15 +73,15 @@ BE_FUNC_CTYPE_DECLARE(be_BLE_set_characteristic, "", "@s");
extern void be_BLE_run(struct bvm *vm, uint8_t operation, bbool response, int32_t arg1); extern void be_BLE_run(struct bvm *vm, uint8_t operation, bbool response, int32_t arg1);
BE_FUNC_CTYPE_DECLARE(be_BLE_run, "", "@i[bi]"); BE_FUNC_CTYPE_DECLARE(be_BLE_run, "", "@i[bi]");
extern void be_BLE_store(uint8_t *buf, size_t size);
BE_FUNC_CTYPE_DECLARE(be_BLE_store, "", "(bytes)~");
extern void be_BLE_set_service(struct bvm *vm, const char *Svc, bbool discoverAttributes); extern void be_BLE_set_service(struct bvm *vm, const char *Svc, bbool discoverAttributes);
BE_FUNC_CTYPE_DECLARE(be_BLE_set_service, "", "@s[b]"); BE_FUNC_CTYPE_DECLARE(be_BLE_set_service, "", "@s[b]");
extern void be_BLE_adv_watch(struct bvm *vm, uint8_t *buf, size_t size, uint8_t type); extern void be_BLE_adv_watch(struct bvm *vm, uint8_t *buf, size_t size, uint8_t type);
BE_FUNC_CTYPE_DECLARE(be_BLE_adv_watch, "", "@(bytes)~[i]"); BE_FUNC_CTYPE_DECLARE(be_BLE_adv_watch, "", "@(bytes)~[i]");
extern void be_BLE_adv_block(struct bvm *vm, uint8_t *buf, size_t size, uint8_t type);
BE_FUNC_CTYPE_DECLARE(be_BLE_adv_block, "", "@(bytes)~[i]");
#include "be_fixed_BLE.h" #include "be_fixed_BLE.h"
@ -93,11 +93,11 @@ module BLE (scope: global) {
conn_cb, ctype_func(be_BLE_reg_conn_cb) conn_cb, ctype_func(be_BLE_reg_conn_cb)
set_svc, ctype_func(be_BLE_set_service) set_svc, ctype_func(be_BLE_set_service)
run, ctype_func(be_BLE_run) run, ctype_func(be_BLE_run)
store, ctype_func(be_BLE_store)
set_chr, ctype_func(be_BLE_set_characteristic) set_chr, ctype_func(be_BLE_set_characteristic)
adv_cb, ctype_func(be_BLE_reg_adv_cb) adv_cb, ctype_func(be_BLE_reg_adv_cb)
set_MAC, ctype_func(be_BLE_set_MAC) set_MAC, ctype_func(be_BLE_set_MAC)
adv_watch, ctype_func(be_BLE_adv_watch) adv_watch, ctype_func(be_BLE_adv_watch)
adv_block, ctype_func(be_BLE_adv_block)
serv_cb, ctype_func(be_BLE_reg_server_cb) serv_cb, ctype_func(be_BLE_reg_server_cb)
} }
@const_object_info_end */ @const_object_info_end */

View File

@ -0,0 +1,42 @@
BasedOnStyle: Google
Language: Cpp
ColumnLimit: 120
IndentWidth: 4
TabWidth: 4
UseTab: Never
SortIncludes: Never
PPIndentWidth : 1
IndentPPDirectives: AfterHash
ReflowComments: true
SpacesBeforeTrailingComments: 1
AlignTrailingComments: true
AlignAfterOpenBracket: Align
AlignConsecutiveMacros:
Enabled: true
AcrossEmptyLines: true
AcrossComments: true
AlignConsecutiveAssignments:
Enabled: true
AcrossEmptyLines: false
AcrossComments: true
AlignCompound: true
PadOperators: true
AlignConsecutiveDeclarations:
Enabled: true
AcrossEmptyLines: false
AcrossComments: true
AlignEscapedNewlines: Left
AccessModifierOffset: -2
AlignArrayOfStructures: Right
AlignOperands: Align
AllowAllArgumentsOnNextLine: false
AllowShortFunctionsOnASingleLine: Inline
AllowShortBlocksOnASingleLine: Empty
BreakBeforeBinaryOperators: None
BinPackArguments: false
BinPackParameters: false
DerivePointerAlignment: false
PenaltyBreakAssignment: 4
PenaltyExcessCharacter: 4
PenaltyBreakBeforeFirstCallParameter: 1
PointerAlignment: Left

View File

@ -1,9 +1,11 @@
name: Build name: Build
on: on:
workflow_dispatch: # Start a workflow workflow_dispatch:
pull_request: pull_request:
push: push:
branches:
- master
jobs: jobs:
build-esp-idf-component: build-esp-idf-component:
@ -15,35 +17,35 @@ jobs:
# See https://hub.docker.com/r/espressif/idf/tags and # See https://hub.docker.com/r/espressif/idf/tags and
# https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/tools/idf-docker-image.html # https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/tools/idf-docker-image.html
# for details. # for details.
idf_ver: ["release-v4.4", "release-v5.1"] idf_ver: ["release-v4.4", "release-v5.4"]
idf_target: ["esp32", "esp32s3", "esp32c2", "esp32c3", "esp32c6"] idf_target: ["esp32", "esp32s3", "esp32c2", "esp32c3", "esp32c5", "esp32c6", "esp32h2", "esp32p4"]
example: example:
- Advanced/NimBLE_Client - NimBLE_Client
- Advanced/NimBLE_Server - NimBLE_Server
- basic/BLE_client
- basic/BLE_notify
- basic/BLE_scan
- basic/BLE_server
- basic/BLE_uart
- Bluetooth_5/NimBLE_extended_client - Bluetooth_5/NimBLE_extended_client
- Bluetooth_5/NimBLE_extended_server - Bluetooth_5/NimBLE_extended_server
- Bluetooth_5/NimBLE_multi_advertiser
exclude: exclude:
- idf_target: "esp32" - idf_target: "esp32"
example: Bluetooth_5/NimBLE_extended_client example: Bluetooth_5/NimBLE_extended_client
- idf_target: "esp32" - idf_target: "esp32"
example: Bluetooth_5/NimBLE_extended_server example: Bluetooth_5/NimBLE_extended_server
- idf_target: "esp32"
example: Bluetooth_5/NimBLE_multi_advertiser
- idf_ver: release-v4.4 - idf_ver: release-v4.4
idf_target: "esp32c2" idf_target: "esp32c2"
- idf_ver: release-v4.4
idf_target: "esp32c5"
- idf_ver: release-v4.4 - idf_ver: release-v4.4
idf_target: "esp32c6" idf_target: "esp32c6"
- idf_ver: release-v4.4
idf_target: "esp32h2"
- idf_ver: release-v4.4
idf_target: "esp32p4"
- idf_ver: release-v5.1
idf_target: "esp32p4"
container: espressif/idf:${{ matrix.idf_ver }} container: espressif/idf:${{ matrix.idf_ver }}
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v3 uses: actions/checkout@v4
with: with:
path: components/esp-nimble-cpp path: components/esp-nimble-cpp
- name: Build examples - name: Build examples
@ -58,8 +60,8 @@ jobs:
build_docs: build_docs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- name: Doxygen Action - name: Doxygen Action
uses: mattnotmitt/doxygen-action@v1.9.5 uses: mattnotmitt/doxygen-action@v1.9.8
with: with:
working-directory: 'docs/' working-directory: 'docs/'

View File

@ -0,0 +1,20 @@
name: Release
on:
release:
types: [published]
jobs:
build_docs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Doxygen Action
uses: mattnotmitt/doxygen-action@v1.9.8
with:
working-directory: 'docs/'
- name: Deploy
uses: peaceiris/actions-gh-pages@v4
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./docs/doxydocs/html

View File

@ -0,0 +1,17 @@
name: Generate Sponsors README
on:
workflow_dispatch:
permissions:
contents: write
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout 🛎️
uses: actions/checkout@v4
- name: Generate Sponsors 💖
uses: JamesIves/github-sponsors-readme-action@v1.5.4
with:
token: ${{ secrets.PAT }}
file: 'README.md'

View File

@ -1,14 +1,336 @@
# Changelog # Changelog
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
## [Unreleased]
## [2.3.0] 2025-05-19
## Fixed
- Incorrect `NimBLECharacteristic::onSubscribe` value when indications are set.
- `NimBLECharacteristic::onRead` callback not called in some cases.
- Clear attribute value when zero length value is written.
- Notify/Indicate incorrectly returning success with custom value.
- Corrected NimBLEClient array initialization.
- Prevent potential exception when scan is restarted.
- Attribute getValue failing with some data types
- Incorrectly passing a pointer to a function taking const reference.
## Added
- Support for esp32c5
- L2CAP infrastructure.
- Scan duplicate cache reset time.
## Changed
- Cleaned up examples.
- Allow PHY updates without enabling extended advertising.
## [2.2.1] 2025-02-28
## Fixed
- Added back `NimBLEClient::connect` overload with `NimBLEAdvertisedDevice` parameter to resolve connection error due to NULL address.
- Crash caused by returning invalid vector entry when retrieving remote descriptors.
## [2.2.0] 2025-02-24
## Fixed
- Crash when calling `NimBLEClient::DiscoverAttributes`.
## Added
- Conditional macros for logging.
- `NimBLEDeviceCallbacks` class with a callback for handling bond storage.
## [2.1.1] 2025-01-26
## Fixed
- remote descriptor discovery error when no descriptors exist.
- scan filter settings not enabled for esp32s3/c3.
- remote descriptor discovery returning more than the desired descriptor.
## [2.1.0] 2025-01-12
## Fixed
- Crash when retrieving descriptors if more than one exists.
- Incorrect TX power value being advertised.
- New user guide code for 2.x
- Potential race condition if `NimBLEScan::clearResults1 is called from multiple tasks.
## Changed
- If privacy is not enabled identity keys will not be shared.
- `NimBLEDevice::setPower` and `NimBLEDevice::getPower` now take an additional parameter `NimBLETxPowerType` to set/get the power level for different operations.
## Added
- Config option `CONFIG_NIMBLE_CPP_ADDR_FMT_EXCLUDE_DELIMITER`, if defined will remove the ":" delimiter from the BLE address string.
- Config option `CONFIG_NIMBLE_CPP_ADDR_FMT_UPPERCASE` if defined will make the BLE address strings uppercase.
## [2.0.3] 2025-01-05
## Fixed
- Unused variable warning when log level is below info.
- Build error missing definition of CONFIG_NIMBLE_CPP_FREERTOS_TASK_BLOCK_BIT in platformio.
- Race condition in `NimBLEScan` that can cause a crash due to heap corruption if `NimBLEScan::stop` is called from the `onResult` callback.
- Advertisement data not set if scan response is enabled after the data is set.
- `NimBLECharacteristic`/`NimBLEDescriptor` not able to update their values in the `onRead` callback.
- Too short of a timeout being requested in NimBLE_Server example leading to frequent disconnects.
## Changed
- `NimBLEHIDDevice` now allows for the same report ID in multiple input/output/feature reports.
## Added
- Config for custom log colors pre level.
- Error logs in the case that NIMBLE_CPP_DEBUG_ASSERT is not defined.
- Error logs when setting advertisement data fails.
- Missing documentation in the migration guide about enabling automatic advertising on disconnect, which was disabled by default in 2.x.
## [2.0.2] 2024-12-21
## Fixed
- Compile error when only advertising role is enabled.
- Possible crash if bonded client reconnects.
## Changed
- `NimBLEHIDDevice` can now create more than one in/out/feature report map.
## [2.0.1] 2024-12-16
## Fixed
- `NimBLEHIDDevice::getOutputReport` will now return the correct characteristic.
- Compile error when central is disabled, class `NimBLEServer` has no member named `m_pClient`.
## Changed
- Added missing includes for `NimBLEConnInfo` and `NimBLEUtils` to `NimBLEDevice.h`.
## [2.0.0] 2024-12-14
## **Breaking changes**
- All connection oriented callbacks now receive a reference to `NimBLEConnInfo`, the `ble_gap_conn_desc` has also been replace with `NimBLEConnInfo` in the functions that received it.
- All functions that take a time input parameter now expect the value to be in milliseconds instead of seconds.
- Removed Eddystone URL as it has been shutdown by google since 2021.
- NimBLESecurity class removed.
- `NimBLEDevice` Ignore list functions removed.
- `NimBLEDevice::startSecurity` now returns a `bool`, true on success, instead of an int to be consistent with the rest of the library.
- `NimBLEDevice::getInitialized` renamed to `NimBLEDevice::isInitialized`.
- `NimBLEDevice::setPower` no longer takes the `esp_power_level_t` and `esp_ble_power_type_t`, instead only an integer value in dbm units is accepted.
- `NimBLEDevice::setOwnAddrType` no longer takes a `bool nrpa` parameter.
- `NimBLEDevice::getClientListSize` replaced with `NimBLEDevice::getCreatedClientCount`.
- `NimBLEDevice::getClientList` was removed.
- `NimBLEServer::disconnect` now returns `bool`, true = success, instead of `int` to be consistent with the rest of the library.
- `NimBLEServerCallbacks::onMTUChanged` renamed to `NimBLEServerCallbacks::onMTUChange` to be consistent with the client callback.
- `NimBLEServer::getPeerIDInfo` renamed to `NimBLEServer::getPeerInfoByHandle` to better describe it's use.
- `NimBLEServerCallbacks::onPassKeyRequest` has been replaced with `NimBLEServer::onPassKeyDisplay` which should display the pairing pin that the client is expected to send.
- `NimBLEServerCallbacks::onAuthenticationComplete` now takes a `NimBLEConnInfo&` parameter.
- `NimBLEClient::getServices` now returns a const reference to std::vector<NimBLERemoteService*> instead of a pointer to the internal vector.
- `NimBLEClient::getConnId` has been renamed to `getConnHandle` to be consistent with bluetooth terminology.
- `NimBLEClient::disconnect` now returns a `bool`, true on success, instead of an int to be consistent with the rest of the library.
- `NimBLEClientCallbacks::onDisconnect` now takes an additional `int reason` parameter to let the application know why the disconnect occurred.
- `NimBLEClientCallbacks::onPassKeyRequest` has been changed to `NimBLEClientCallbacks::onPassKeyEntry` which takes a `NimBLEConnInfo&` parameter and does not return a value. Instead or returning a value this callback should prompt a user to enter a pin number which is sent later via `NimBLEDevice::injectPassKey`.
- `NimBLEClientCallbacks::onConfirmPIN` renamed to `NimBLEClientCallbacks::onConfirmPasskey` and no longer returns a value and now takes a `NimBLEConnInfo&` parameter. This should be used to prompt a user to confirm the pin on the display (YES/NO) after which the response should be sent with `NimBLEDevice::injectConfirmPasskey`.
- `NimBLEAdvertising::setMinPreferred` and `NimBLEAdvertising::setMaxPreferred` have been removed, use `NimBLEAdvertising::setPreferredParams` instead.
- Advertising the name and TX power of the device will no longer happen by default and should be set manually by the application.
- `NimBLEAdvertising::setAdvertisementType` has been renamed to `NimBLEAdvertising::setConnectableMode` to better reflect it's function.
- `NimBLEAdvertising::setScanResponse` has been renamed to `NimBLEAdvertising::enableScanResponse` to better reflect it's function.
- `NimBLEAdvertising`; Scan response is no longer enabled by default.
- `NimBLEAdvertising::start` No longer takes a callback pointer parameter, instead the new method `NimBLEAdvertising::setAdvertisingCompleteCallback` should be used.
- `NimBLEAdvertisementData::addData` now takes either a `std::vector<uint8_t>` or `uint8_t* + length` instead of `std::string` or `char + length`.
- `NimBLEAdvertisementData::getPayload` now returns `std::vector<uint8_t>` instead of `std::string`.
- The callback parameter for `NimBLEScan::start` has been removed and the blocking overload of `NimBLEScan::start` has been replaced by an overload of `NimBLEScan::getResults` with the same parameters.
- `NimBLEAdvertisedDeviceCallbacks` Has been replaced by `NimBLEScanCallbacks` which contains the following methods: `onResult`, `onScanEnd`, and `onDiscovered
- - `NimBLEScanCallbacks::onResult`, functions the same as the old `NimBLEAdvertisedDeviceCallbacks::onResult` but now takes aa `const NimBLEAdvertisedDevice*` instead of non-const.
- - `NimBLEScanCallbacks::onScanEnd`, replaces the scanEnded callback passed to `NimBLEScan::start` and now takes a `const NimBLEScanResults&` and `int reason` parameter.
- - `NimBLEScanCallbacks::onDiscovered`, This is called immediately when a device is first scanned, before any scan response data is available and takes a `const NimBLEAdvertisedDevice*` parameter.
- `NimBLEScan::stop` will no longer call the `onScanEnd` callback as the caller should know its been stopped when this is called.
- `NimBLEScan::clearDuplicateCache` has been removed as it was problematic and only for the esp32. Stop and start the scanner for the same effect.
- `NimBLEScanResults::getDevice` methods now return `const NimBLEAdvertisedDevice*`.
- `NimBLEScanResults` iterators are now `const_iterator`.
- `NimBLEAdvertisedDevice::hasRSSI` removed as redundant, RSSI is always available.
- `NimBLEAdvertisedDevice::getPayload` now returns `const std::vector<uint8_t>` instead of a pointer to internal memory.
- `NimBLEAdvertisedDevice` Timestamp removed, if needed then the app should track the time from the callback.
- `NimBLECharacteristic::notify` no longer takes a `bool is_notification` parameter, instead `indicate()` should be called to send indications.
- `NimBLECharacteristicCallbacks::onNotify` removed as unnecessary, the library does not call notify without app input.
- `NimBLECharacteristicCallbacks::onStatus` No longer takes a `status` parameter, refer to the return code for success/failure.
- `NimBLERemoteCharacteristic::getRemoteService` now returns a `const NimBLERemoteService*` instead of non-const.
- `NimBLERemoteCharacteristic::readUInt32` Has been removed.
- `NimBLERemoteCharacteristic::readUInt16` Has been removed.
- `NimBLERemoteCharacteristic::readUInt8` Has been removed.
- `NimBLERemoteCharacteristic::readFloat` Has been removed.
- `NimBLERemoteCharacteristic::registerForNotify` Has been removed.
- `NimBLERemoteService::getCharacteristics` now returns a `const std::vector<NimBLERemoteCharacteristic*>&` instead of non-const `std::vector<NimBLERemoteCharacteristic*>*`.
- `NimBLERemoteService::getValue` now returns `NimBLEAttValue` instead of `std::string`.
- `NimBLEService::getCharacteristics` now returns a `const std::vector<NimBLECharacteristic*>&` instead of std::vector<NimBLECharacteristic *>.
- `NimBLEUUID::getNative` method replaced with `NimBLEUUID::getBase` which returns a read-only pointer to the underlying `ble_uuid_t` struct.
- `NimBLEUUID`; `msbFirst` parameter has been removed from constructor, caller should reverse the data first or call the new `reverseByteOrder` method after.
- `NimBLEAddress` constructor; default value for the `type` parameter removed, caller should know the address type and specify it.
- `NimBLEAddress::getNative` replaced with `NimBLEAddress::getBase` and now returns a pointer to `const ble_addr_t` instead of a pointer to the address value.
- `NimBLEAddress::equals` method and `NimBLEAddress::== operator` will now also test if the address types are the same.
- `NimBLEUtils::dumpGapEvent` function removed.
- `NimBLEUtils::buildHexData` replaced with `NimBLEUtils::dataToHexString`, which returns a `std::string` containing the hex string.
- `NimBLEEddystoneTLM::setTemp` now takes an `int16_t` parameter instead of float to be friendly to devices without floating point support.
- `NimBLEEddystoneTLM::getTemp` now returns `int16_t` to work with devices that don't have floating point support.
- `NimBLEEddystoneTLM::setData` now takes a reference to * `NimBLEEddystoneTLM::BeaconData` instead of `std::string`.
- `NimBLEEddystoneTLM::getData` now returns a reference to * `NimBLEEddystoneTLM::BeaconData` instead of `std::string`.
- `NimBLEBeacon::setData` now takes `const NimBLEBeacon::BeaconData&` instead of `std::string`.
- `NimBLEBeacon::getData` now returns `const NimBLEBeacon::BeaconData&` instead of `std::string`.
- `NimBLEHIDDevice::reportMap` renamed to `NimBLEHIDDevice::getReportMap`.
- `NimBLEHIDDevice::hidControl` renamed to `NimBLEHIDDevice::getHidControl`.
- `NimBLEHIDDevice::inputReport`renamed to `NimBLEHIDDevice::getInputReport`.
- `NimBLEHIDDevice::outputReport`renamed to `NimBLEHIDDevice::getOutputReport`.
- `NimBLEHIDDevice::featureReport`renamed to `NimBLEHIDDevice::getFeatureReport`.
- `NimBLEHIDDevice::protocolMode`renamed to `NimBLEHIDDevice::getProtocolMode`.
- `NimBLEHIDDevice::bootOutput`renamed to `NimBLEHIDDevice::getBootOutput`.
- `NimBLEHIDDevice::pnp`renamed to `NimBLEHIDDevice::setPnp`.
- `NimBLEHIDDevice::hidInfo`renamed to `NimBLEHIDDevice::setHidInfo`.
- `NimBLEHIDDevice::deviceInfo`renamed to `NimBLEHIDDevice::getDeviceInfoService`.
- `NimBLEHIDDevice::hidService`renamed to `NimBLEHIDDevice::getHidService`.
- `NimBLEHIDDevice::batteryService`renamed to `NimBLEHIDDevice::getBatteryService`.
## Fixed
- `NimBLEDevice::getPower` and `NimBLEDevice::getPowerLevel` bug worked around for the esp32s3 and esp32c3.
- `NimBLEDevice::setPower` and `NimBLEDevice::getPower` now support the full power range for all esp devices.
- `NimBLEDevice::setOwnAddrType` will now correctly apply the provided address type for all devices.
- `NimBLEDevice::getPower` (esp32) return value is now calculated to support devices with different min/max ranges.
- Crash if `NimBLEDevice::deinit` is called when the stack has not been initialized.
- `NimBLEServer` service changed notifications will now wait until the changes have taken effect and the server re-started before indicating the change to peers, reducing difficultly for some clients to update their data.
- `NimBLEService::getHandle` will now fetch the handle from the stack if not valid to avoid returning an invalid value.
- `std::vector` input to set/write values template.
- `NimBLEHIDDevice::pnp` will now set the data correctly.
- Check for Arduino component
- Fixed building with esp-idf version 5.x.
- Fixed pairing failing when the process was started by the peer first.
- Fixed building with esp-idf and Arduino component.
- Workaround for esp32s3 and esp32c3 not returning the correct txPower with some IDF versions.
### Changed ### Changed
- NimBLESecurity class removed. - `NimBLEClient::secureConnection` now takes an additional parameter `bool async`, if true, will send the secure command and return immediately with a true value for successfully sending the command, else false. This allows for asynchronously securing a connection.
- Deleting the client instance from the `onDisconnect` callback is now supported.
- `NimBLEClient::connect` will no longer cancel already in progress connections.
- `NimBLEClient::setDataLen` now returns bool, true if successful.
- `NimBLEClient::updateConnParams` now returns bool, true if successful.
- `NimBLEClient::setPeerAddress` now returns a bool, true on success.
- `NimBLEDevice::startSecurity` now takes and additional parameter `int* rcPtr` which will provide the return code from the stack if provided.
- `NimBLEDevice::deleteClient` no longer blocks tasks.
- `NimBLEDevice::getAddress` will now return the address currently in use.
- `NimBLEDevice::init` now returns a bool with `true` indicating success.
- `NimBLEDevice::deinit` now returns a bool with `true` indicating success.
- `NimBLEDevice::setDeviceName` now returns a bool with `true` indicating success.
- `NimBLEDevice::setCustomGapHandler` now returns a bool with `true` indicating success.
- `NimBLEDevice::setOwnAddrType` now returns a bool with `true` indicating success and works with non-esp32 devices.
- `NimBLEDevice::setPower` now returns a bool value, true = success.
- `NimBLEDevice::setMTU` now returns a bool value, true = success.
- `NimBLEDevice::deleteAllBonds` now returns true on success, otherwise false.
- `NimBLEEddystoneTLM` internal data struct type `BeaconData` is now public and usable by the application.
- `NimBLEBeacon` internal data struct type `BeaconData` is now public and can be used by the application.
- Removed tracking of client characteristic subscription status from `NimBLEServer` and `NimBLECharacteristic` and instead uses
the functions and tracking in the host stack.
- `NimBLECharacteristic::indicate` now takes the same parameters as `notify`.
- `NimBLECharacteristic::notify` and `NimBLECharacteristic::indicate` now return a `bool`, true = success.
- Added optional `connHandle` parameter to `NimBLECharacteristic::notify` to allow for sending notifications to specific clients.
- `NimBLEServer` Now uses a std::array to store client connection handles instead of std::vector to reduce memory allocation.
- `NimBLEExtAdvertisement` : All functions that set data now return `bool`, true = success.
- `NimBLEAdvertising` Advertising data is now stored in instances of `NimBLEAdvertisingData` and old vectors removed.
- `NimBLEAdvertising::setAdvertisementData` and `NimBLEAdvertising::setScanResponseData` now return `bool`, true = success.
- Added optional `NimBLEAddress` parameter to `NimBLEAdvertising::start` to allow for directed advertising to a peer.
- All `NimBLEAdvertising` functions that change data values now return `bool`, true = success.
- All `NimBLEAdvertisementData` functions that change data values now return `bool`, true = success.
- `NimBLEAdvertising` advertising complete callback is now defined as std::function to allow for using std::bind for callback functions.
- `NimBLEAdvertisementData::setName` now takes an optional `bool` parameter to indicate if the name is complete or incomplete, default = complete.
- `NimBLEAdvertisementData` moved to it's own .h and .cpp files.
- `NimBLEScan::start` takes a new `bool restart` parameter, default `true`, that will restart an already in progress scan and clear the duplicate filter so all devices will be discovered again.
- Scan response data that is received without advertisement first will now create the device and send a callback.
- `NimBLEScan::start` will no longer clear cache or results if scanning is already in progress.
- `NimBLEScan::start` will now allow the start command to be sent with updated parameters if already scanning.
- `NimBLEScan::clearResults` will now reset the vector capacity to 0.
- Host reset will now no longer restart scanning and instead will call `NimBLEScanCallbacks::onScanEnd`.
- Added optional `index` parameter to `NimBLEAdvertisedDevice::getPayloadByType`
- `NimBLEAdvertisedDevice::getManufacturerData` now takes an optional index parameter for use in the case of multiple manufacturer data fields.
- `NimBLEUtils`: Add missing GAP event strings.
- `NimBLEUtils`: Add missing return code strings.
- `NimBLEUtils`: Event/error code strings optimized.
- `NimBLEAttValue` cleanup and optimization.
- cleaned up code, removed assert/abort calls, replaced with a configurable option to enable debug asserts.
### Added ### Added
- (esp32 specific) `NimBLEDevice::setPowerLevel` and `NimBLEDevice::getPowerLevel` which take and return the related `esp_power_level* ` types.
- `NimBLEDevice::setDefaultPhy` which will set the default preferred PHY for all connections.
- `NimBLEDevice::getConnectedClients`, which returns a vector of pointers to the currently connected client instances.
- `NimBLEDevice::setOwnAddr` function added, which takes a `uint8_t*` or `NimBLEAddress&` and will set the mac address of the device, returns `bool` true= success.
- `NimBLEDevice::injectPassKey` Used to send the pairing passkey instead of a return value from the client callback.
- `NimBLEDevice::injectConfirmPasskey` Used to send the numeric comparison pairing passkey confirmation instead of a return value from the client callback.
- `NimBLEDevice::setDeviceName` to change the device name after initialization. - `NimBLEDevice::setDeviceName` to change the device name after initialization.
- `NimBLECharacteristic::create2904` which will specifically create a Characteristic Presentation Format (0x2904) descriptor.
- `NimBLEAdvertising::refreshAdvertisingData` refreshes the advertisement data while still actively advertising.
- `NimBLEClient::updatePhy` to request a PHY change with a peer.
- `NimBLEClient::getPhy` to read the current connection PHY setting.
- `Config` struct to `NimBLEClient` to efficiently set single bit config settings.
- `NimBLEClient::setSelfDelete` that takes the bool parameters `deleteOnDisconnect` and `deleteOnConnectFail`, which will configure the client to delete itself when disconnected or the connection attempt fails.
- `NimBLEClient::setConfig` and `NimBLEClient::getConfig` which takes or returns a `NimBLEClient::Config` object respectively.
- `NimBLEClient::cancelConnect()` to cancel an in-progress connection, returns `bool`, true = success.
- Non-blocking `NimBLEClient::connect` option added via 2 new `bool` parameters added to the function:
- * `asyncConnect`; if true, will send the connect command and return immediately.
- * `exchangeMTU`; if true will send the exchange MTU command upon connection.
- `NimBLEClientCallbacks::onConnectFail` callback that is called when the connection attempt fail while connecting asynchronously.
- `NimBLEClientCallbacks::onMTUChange` callback which will be called when the MTU exchange completes and takes a `NimBLEClient*` and `uint16_t MTU` parameter.
- `NimBLEClientCallbacks::onPhyUpdate` and -`NimBLEServerCallbacks::onPhyUpdate` Which are called when the PHY update is complete.
- Extended scan example.
- `NimBLEServer::updatePhy` to request a PHY change with a peer.
- `NimBLEServer::getPhy` to read the PHY of a peer connection.
- `NimBLEServer::getClient` which will create a client instance from the provided peer connHandle or connInfo to facilitate reading/write from the connected client.
- `NimBLEServerCallbacks::onConnParamsUpdate` callback.
- `NimBLEScan::erase` overload that takes a `const NimBLEAdvertisedDevice*` parameter.
- `NimBLEScan::setScanPhy` to enable/disable the PHY's to scan on (extended advertising only).
- `NimBLEScan::setScanPeriod` which will allow for setting a scan restart timer in the controller (extended advertising only).
- `NimBLEAdvertisedDevice::isScannable()` that returns true if the device is scannable.
- `NimBLEAdvertisedDevice::begin` and `NimBLEAdvertisedDevice::end` read-only iterators for convenience and use in range loops.
- `NimBLEAdvertisedDevice::getAdvFlags` returns the advertisement flags of the advertiser.
- `NimBLEAdvertisedDevice::getPayloadByType` Generic use function that returns the data from the advertisement with the specified type.
- `NimBLEAdvertisedDevice::haveType` Generic use function that returns true if the advertisement data contains a field with the specified type.
- Support for esp32c6, esp32c2, esp32h2, and esp32p4.
- `NimBLEExtAdvertisement::removeData`, which will remove the data of the specified type from the advertisement.
- `NimBLEExtAdvertisement::addServiceUUID`, which will append to the service uuids advertised.
- `NimBLEExtAdvertisement::removeServiceUUID`, which will remove the service from the uuids advertised.
- `NimBLEExtAdvertisement::removeServices`, which will remove all service uuids advertised.
- New overloads for `NimBLEExtAdvertisement::setServiceData` with the parameters `const NimBLEUUID& uuid, const uint8_t* data, size_t length` and `const NimBLEUUID& uuid, const std::vector<uint8_t>& data`.
- `NimBLEExtAdvertisement::getDataLocation`, which returns the location in the advertisement data of the type requested in parameter `uint8_t type`.
- `NimBLEExtAdvertisement::toString` which returns a hex string representation of the advertisement data.
- `NimBLEAdvertising::getAdvertisementData`, which returns a reference to the currently set advertisement data.
- `NimBLEAdvertising::getScanData`, which returns a reference to the currently set scan response data.
- New overloads for `NimBLEAdvertising::removeServiceUUID` and `NimBLEAdvertisementData::removeServiceUUID` to accept a `const char*`
- `NimBLEAdvertising::clearData`, which will clear the advertisement and scan response data.
- `NimBLEAdvertising::setManufacturerData` Overload that takes a `const uint8_t*` and , size_t` parameter.
- `NimBLEAdvertising::setServiceData` Overload that takes `const NimBLEUUID& uuid`, ` const uint8_t* data`, ` size_t length` as parameters.
- `NimBLEAdvertising::setServiceData` Overload that takes `const NimBLEUUID& uuid`, `const std::vector<uint8_t>&` as parameters.
- `NimBLEAdvertising::setDiscoverableMode` to allow applications to control the discoverability of the advertiser.
- `NimBLEAdvertising::setAdvertisingCompleteCallback` sets the callback to call when advertising ends.
- `NimBLEAdvertising::setPreferredParams` that takes the min and max preferred connection parameters as an alternative for `setMinPreferred` and `setMaxPreferred`.
- `NimBLEAdvertising::setAdvertisingInterval` Sets the advertisement interval for min and max to the same value instead of calling `setMinInterval` and `setMaxInterval` separately if there is not value difference.
- `NimBLEAdvertisementData::removeData`, which takes a parameter `uint8_t type`, the data type to remove.
- `NimBLEAdvertisementData::toString`, which will print the data in hex.
- `NimBLEUtils::taskWait` which causes the calling task to wait for an event.
- `NimBLEUtils::taskRelease` releases the task from and event.
- `NimBLEUtils::generateAddr` function added with will generate a random address and takes a `bool` parameter, true = create non-resolvable private address, otherwise a random static address is created, returns `NimBLEAddress`.
- `NimBLEUUID::getValue` which returns a read-only `uint8_t` pointer to the UUID value.
- `NimBLEUUID::reverseByteOrder`, this will reverse the bytes of the UUID, which can be useful for advertising/logging.
- `NimBLEUUID` constructor overload that takes a reference to `ble_uuid_any_t`.
- `NimBLEAddress::isNrpa` method to test if an address is random non-resolvable.
- `NimBLEAddress::isStatic` method to test if an address is random static.
- `NimBLEAddress::isPublic` method to test if an address is a public address.
- `NimBLEAddress::isNull` methods to test if an address is NULL.
- `NimBLEAddress::getValue` method which returns a read-only pointer to the address value.
- `NimBLEAddress::reverseByteOrder` method which will reverse the byte order of the address value.
- `NimBLEHIDDevice::batteryLevel` returns the HID device battery level characteristic. - `NimBLEHIDDevice::batteryLevel` returns the HID device battery level characteristic.
- `NimBLEBeacon::setData` overload that takes `uint8_t* data` and `uint8_t length`.
- `NimBLEHIDDevice::getPnp` function added to access the pnp characteristic.
- `NimBLEHIDDevice::getHidInfo` function added to access the hid info characteristic.
## [1.4.1] - 2022-10-30
### Fixed
- NimBLEDevice::getPower incorrect value when power level is -3db.
- Failed pairing when already in progress.
### Changed
- Revert previous change that forced writing with response when subscribing in favor of allowing the application to decide.
### Added
- Added NimBLEHIDDevice::batteryLevel.
- Added NimBLEDevice::setDeviceName allowing for changing the device name while the BLE stack is active.
- CI Builds
## [1.4.0] - 2022-07-31 ## [1.4.0] - 2022-07-31
@ -71,7 +393,7 @@ All notable changes to this project will be documented in this file.
## [1.3.0] - 2021-08-02 ## [1.3.0] - 2021-08-02
### Added ### Added
- `NimBLECharacteristic::removeDescriptor`: Dynamically remove a descriptor from a characterisic. Takes effect after all connections are closed and sends a service changed indication. - `NimBLECharacteristic::removeDescriptor`: Dynamically remove a descriptor from a characteristic. Takes effect after all connections are closed and sends a service changed indication.
- `NimBLEService::removeCharacteristic`: Dynamically remove a characteristic from a service. Takes effect after all connections are closed and sends a service changed indication - `NimBLEService::removeCharacteristic`: Dynamically remove a characteristic from a service. Takes effect after all connections are closed and sends a service changed indication
- `NimBLEServerCallbacks::onMTUChange`: This is callback is called when the MTU is updated after connection with a client. - `NimBLEServerCallbacks::onMTUChange`: This is callback is called when the MTU is updated after connection with a client.
- ESP32C3 support - ESP32C3 support
@ -102,12 +424,12 @@ All notable changes to this project will be documented in this file.
### Fixed ### Fixed
- `NimBLECharacteristicCallbacks::onSubscribe` Is now called after the connection is added to the vector. - `NimBLECharacteristicCallbacks::onSubscribe` Is now called after the connection is added to the vector.
- Corrected bonding failure when reinitializing the BLE stack. - Corrected bonding failure when reinitializing the BLE stack.
- Writing to a characterisic with a std::string value now correctly writes values with null characters. - Writing to a characteristic with a std::string value now correctly writes values with null characters.
- Retrieving remote descriptors now uses the characterisic end handle correctly. - Retrieving remote descriptors now uses the characteristic end handle correctly.
- Missing data in long writes to remote descriptors. - Missing data in long writes to remote descriptors.
- Hanging on task notification when sending an indication from the characteristic callback. - Hanging on task notification when sending an indication from the characteristic callback.
- BLE controller memory could be released when using Arduino as a component. - BLE controller memory could be released when using Arduino as a component.
- Complile errors with NimBLE release 1.3.0. - Compile errors with NimBLE release 1.3.0.
## [1.2.0] - 2021-02-08 ## [1.2.0] - 2021-02-08
@ -120,7 +442,7 @@ All notable changes to this project will be documented in this file.
- `NimBLEService::getCharacteristicByHandle`: Get a pointer to the characteristic object with the specified 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. - `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) 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 char *uuid)`
- `NimBLEService::getCharacteristics(const NimBLEUUID &uuid)` - `NimBLEService::getCharacteristics(const NimBLEUUID &uuid)`
@ -162,12 +484,12 @@ Overloads to get a vector containing pointers to all the characteristics in a se
- `NimBLEAdvertising` Transmission power is no longer advertised by default and can be added to the advertisement by calling `NimBLEAdvertising::addTxPower` - `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. - `NimBLEAdvertising` Custom scan response data can now be used without custom advertisement.
- `NimBLEScan` Now uses the controller duplicate filter. - `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. - `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. Instead the data will be parsed on-demand when the user application asks for specific data.
### Fixed ### Fixed
- `NimBLEHIDDevice` Characteristics now use encryption, this resolves an issue with communicating with devices requiring encryption for HID devices. - `NimBLEHIDDevice` Characteristics now use encryption, this resolves an issue with communicating with devices requiring encryption for HID devices.
@ -176,84 +498,84 @@ Instead the data will be parsed on-demand when the user application asks for spe
## [1.1.0] - 2021-01-20 ## [1.1.0] - 2021-01-20
### Added ### Added
- `NimBLEDevice::setOwnAddrType` added to enable the use of random and random-resolvable addresses, by asukiaaa - `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. - New examples for securing and authenticating client/server connections, by mblasee.
- `NimBLEAdvertising::SetMinPreferred` and `NimBLEAdvertising::SetMinPreferred` re-added. - `NimBLEAdvertising::SetMinPreferred` and `NimBLEAdvertising::SetMinPreferred` re-added.
- Conditional checks added for command line config options in `nimconfig.h` to support custom configuration in platformio. - 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::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 - `NimBLEClient::getCharacteristic(uint16_t handle)` Enabling the use of the characteristic handle to be used to find
the NimBLERemoteCharacteristic object. the NimBLERemoteCharacteristic object.
- `NimBLEHIDDevice` class added by wakwak-koba. - `NimBLEHIDDevice` class added by wakwak-koba.
- `NimBLEServerCallbacks::onDisconnect` overloaded callback added to provide a ble_gap_conn_desc parameter for the application - `NimBLEServerCallbacks::onDisconnect` overloaded callback added to provide a ble_gap_conn_desc parameter for the application
to obtain information about the disconnected client. to obtain information about the disconnected client.
- Conditional checks in `nimconfig.h` for command line defined macros to support platformio config settings. - Conditional checks in `nimconfig.h` for command line defined macros to support platformio config settings.
### Changed ### Changed
- `NimBLEAdvertising::start` now returns a bool value to indicate success/failure. - `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. - 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. - 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 - `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. regardless of the existence 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. - 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) - `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. this allows the starting of a new scan from the callback function.
### Fixed ### Fixed
- Sometimes `NimBLEClient::connect` would hang on the task block if no event arrived to unblock. - Sometimes `NimBLEClient::connect` would hang on the task block if no event arrived to unblock.
A time limit has been added to timeout appropriately. 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 - When getting descriptors for a characteristic 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. 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 - 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. 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 - An exception could occur after a host reset event when the host re-synced if the tasks that were stopped during the event did
advertised them as 16/32bit but resolved them to 128bits. Both are now checked. not finish processing. A yield has been added after re-syncing to allow tasks to finish before proceeding.
- `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. - 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.
- Advertisement type now correctly set when using non-connectable (advertiser only) mode. - Occasionally the call to start scanning would get stuck in a loop on BLE_HS_EBUSY, this loop has been removed.
- Advertising payload length correction, now accounts for appearance. - 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.
- (Arduino) Ensure controller mode is set to BLE Only. - `FreeRTOS` compile errors resolved in latest Arduino 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 ## [1.0.2] - 2020-09-13
### Changed ### Changed
- `NimBLEAdvertising::start` Now takes 2 optional parameters, the first is the duration to advertise for (in seconds), the second is a - `NimBLEAdvertising::start` Now takes 2 optional parameters, the first is the duration to advertise for (in seconds), the second is a
callback that is invoked when advertsing ends and takes a pointer to a `NimBLEAdvertising` object (similar to the `NimBLEScan::start` API). callback that is invoked when advertising ends and takes a pointer to a `NimBLEAdvertising` object (similar to the `NimBLEScan::start` API).
- (Arduino) Maximum BLE connections can now be altered by only changing the value of `CONFIG_BT_NIMBLE_MAX_CONNECTIONS` in `nimconfig.h`. - (Arduino) Maximum BLE connections can now be altered by only changing the value of `CONFIG_BT_NIMBLE_MAX_CONNECTIONS` in `nimconfig.h`.
Any changes to the controller max connection settings in `sdkconfig.h` will now have no effect when using this library. Any changes to the controller max connection settings in `sdkconfig.h` will now have no effect when using this library.
- (Arduino) Revert the previous change to fix the advertising start delay. Instead a replacement fix that routes all BLE controller commands from - (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. a task running on core 0 (same as the controller) has been implemented. This improves response times and reliability for all BLE functions.

View File

@ -2,23 +2,28 @@
# CMakeLists in this exact order for cmake to work correctly # CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5) cmake_minimum_required(VERSION 3.5)
idf_build_get_property(__hack_component_targets __COMPONENT_TARGETS) if(__COMPONENT_TARGETS MATCHES "___idf_esp-nimble-component")
if("esp-nimble-component" IN_LIST BUILD_COMPONENTS OR "__esp-nimble-component" IN_LIST __hack_component_targets)
list(APPEND ESP_NIMBLE_PRIV_REQUIRES list(APPEND ESP_NIMBLE_PRIV_REQUIRES
esp-nimble-component esp-nimble-component
) )
elseif("nimble" IN_LIST BUILD_COMPONENTS OR "__nimble" IN_LIST __hack_component_targets) elseif(__COMPONENT_TARGETS MATCHES "__idf_nimble")
list(APPEND ESP_NIMBLE_PRIV_REQUIRES list(APPEND ESP_NIMBLE_PRIV_REQUIRES
nimble nimble
) )
endif() endif()
if("arduino" IN_LIST BUILD_COMPONENTS OR __hack_component_targets MATCHES "__idf_arduino") # Arduino install using IDF component manager
if(__COMPONENT_TARGETS MATCHES "___idf_espressif__arduino-esp32")
list(APPEND ESP_NIMBLE_PRIV_REQUIRES
arduino-esp32
)
# Manual installation of Arduino framework
elseif(__COMPONENT_TARGETS MATCHES "__idf_arduino")
list(APPEND ESP_NIMBLE_PRIV_REQUIRES list(APPEND ESP_NIMBLE_PRIV_REQUIRES
arduino arduino
) )
elseif("framework-arduinoespressif32" IN_LIST BUILD_COMPONENTS OR __hack_component_targets MATCHES "___idf_framework-arduinoespressif32") # PlatformIO
elseif(__COMPONENT_TARGETS MATCHES "___idf_framework-arduinoespressif32")
list(APPEND ESP_NIMBLE_PRIV_REQUIRES list(APPEND ESP_NIMBLE_PRIV_REQUIRES
framework-arduinoespressif32 framework-arduinoespressif32
) )
@ -30,27 +35,33 @@ idf_component_register(
"esp32s3" "esp32s3"
"esp32c2" "esp32c2"
"esp32c3" "esp32c3"
"esp32c5"
"esp32c6" "esp32c6"
"esp32h2" "esp32h2"
"esp32p4"
INCLUDE_DIRS INCLUDE_DIRS
"src" "src"
SRCS SRCS
"src/NimBLE2904.cpp" "src/NimBLE2904.cpp"
"src/NimBLEAddress.cpp" "src/NimBLEAddress.cpp"
"src/NimBLEAdvertisedDevice.cpp" "src/NimBLEAdvertisedDevice.cpp"
"src/NimBLEAdvertisementData.cpp"
"src/NimBLEAdvertising.cpp" "src/NimBLEAdvertising.cpp"
"src/NimBLEAttValue.cpp"
"src/NimBLEBeacon.cpp" "src/NimBLEBeacon.cpp"
"src/NimBLECharacteristic.cpp" "src/NimBLECharacteristic.cpp"
"src/NimBLEClient.cpp" "src/NimBLEClient.cpp"
"src/NimBLEDescriptor.cpp" "src/NimBLEDescriptor.cpp"
"src/NimBLEDevice.cpp" "src/NimBLEDevice.cpp"
"src/NimBLEEddystoneTLM.cpp" "src/NimBLEEddystoneTLM.cpp"
"src/NimBLEEddystoneURL.cpp"
"src/NimBLEExtAdvertising.cpp" "src/NimBLEExtAdvertising.cpp"
"src/NimBLEHIDDevice.cpp" "src/NimBLEHIDDevice.cpp"
"src/NimBLEL2CAPChannel.cpp"
"src/NimBLEL2CAPServer.cpp"
"src/NimBLERemoteCharacteristic.cpp" "src/NimBLERemoteCharacteristic.cpp"
"src/NimBLERemoteDescriptor.cpp" "src/NimBLERemoteDescriptor.cpp"
"src/NimBLERemoteService.cpp" "src/NimBLERemoteService.cpp"
"src/NimBLERemoteValueAttribute.cpp"
"src/NimBLEScan.cpp" "src/NimBLEScan.cpp"
"src/NimBLEServer.cpp" "src/NimBLEServer.cpp"
"src/NimBLEService.cpp" "src/NimBLEService.cpp"

View File

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

View File

@ -26,6 +26,113 @@ config NIMBLE_CPP_LOG_LEVEL
default 3 if NIMBLE_CPP_LOG_LEVEL_INFO default 3 if NIMBLE_CPP_LOG_LEVEL_INFO
default 4 if NIMBLE_CPP_LOG_LEVEL_DEBUG default 4 if NIMBLE_CPP_LOG_LEVEL_DEBUG
config NIMBLE_CPP_LOG_OVERRIDE_COLOR
bool "Enable log color override."
default "n"
help
Enabling this option will allow NimBLE log levels to have
specific colors assigned.
menu "NIMBLE Log Override Colors"
depends on NIMBLE_CPP_LOG_OVERRIDE_COLOR
choice NIMBLE_CPP_LOG_OVERRIDE_COLOR_ERR
prompt "NimBLE CPP log override color Error"
default NIMBLE_CPP_LOG_OVERRIDE_COLOR_ERR_NONE
help
Select NimBLE CPP log override error color.
config NIMBLE_CPP_LOG_OVERRIDE_COLOR_ERR_NONE
bool "None"
config NIMBLE_CPP_LOG_OVERRIDE_COLOR_ERR_BLACK
bool "Black"
config NIMBLE_CPP_LOG_OVERRIDE_COLOR_ERR_RED
bool "Red"
config NIMBLE_CPP_LOG_OVERRIDE_COLOR_ERR_GREEN
bool "Green"
config NIMBLE_CPP_LOG_OVERRIDE_COLOR_ERR_YELLOW
bool "Yellow"
config NIMBLE_CPP_LOG_OVERRIDE_COLOR_ERR_BLUE
bool "Blue"
config NIMBLE_CPP_LOG_OVERRIDE_COLOR_ERR_PURPLE
bool "Purple"
config NIMBLE_CPP_LOG_OVERRIDE_COLOR_ERR_CYAN
bool "Cyan"
endchoice #NIMBLE_CPP_LOG_OVERRIDE_COLOR_ERR
choice NIMBLE_CPP_LOG_OVERRIDE_COLOR_WARN
prompt "NimBLE CPP log override color Warning"
default NIMBLE_CPP_LOG_OVERRIDE_COLOR_WARN_NONE
help
Select NimBLE CPP log override warning color.
config NIMBLE_CPP_LOG_OVERRIDE_COLOR_WARN_NONE
bool "None"
config NIMBLE_CPP_LOG_OVERRIDE_COLOR_WARN_BLACK
bool "Black"
config NIMBLE_CPP_LOG_OVERRIDE_COLOR_WARN_RED
bool "Red"
config NIMBLE_CPP_LOG_OVERRIDE_COLOR_WARN_GREEN
bool "Green"
config NIMBLE_CPP_LOG_OVERRIDE_COLOR_WARN_YELLOW
bool "Yellow"
config NIMBLE_CPP_LOG_OVERRIDE_COLOR_WARN_BLUE
bool "Blue"
config NIMBLE_CPP_LOG_OVERRIDE_COLOR_WARN_PURPLE
bool "Purple"
config NIMBLE_CPP_LOG_OVERRIDE_COLOR_WARN_CYAN
bool "Cyan"
endchoice #NIMBLE_CPP_LOG_OVERRIDE_COLOR_WARN
choice NIMBLE_CPP_LOG_OVERRIDE_COLOR_INFO
prompt "NimBLE CPP log override color Info"
default NIMBLE_CPP_LOG_OVERRIDE_COLOR_INFO_NONE
help
Select NimBLE CPP log override info color.
config NIMBLE_CPP_LOG_OVERRIDE_COLOR_INFO_NONE
bool "None"
config NIMBLE_CPP_LOG_OVERRIDE_COLOR_INFO_BLACK
bool "Black"
config NIMBLE_CPP_LOG_OVERRIDE_COLOR_INFO_RED
bool "Red"
config NIMBLE_CPP_LOG_OVERRIDE_COLOR_INFO_GREEN
bool "Green"
config NIMBLE_CPP_LOG_OVERRIDE_COLOR_INFO_YELLOW
bool "Yellow"
config NIMBLE_CPP_LOG_OVERRIDE_COLOR_INFO_BLUE
bool "Blue"
config NIMBLE_CPP_LOG_OVERRIDE_COLOR_INFO_PURPLE
bool "Purple"
config NIMBLE_CPP_LOG_OVERRIDE_COLOR_INFO_CYAN
bool "Cyan"
endchoice #NIMBLE_CPP_LOG_OVERRIDE_COLOR_INFO
choice NIMBLE_CPP_LOG_OVERRIDE_COLOR_DEBUG
prompt "NimBLE CPP log override color Debug"
default NIMBLE_CPP_LOG_OVERRIDE_COLOR_DEBUG_NONE
help
Select NimBLE CPP log override debug color.
config NIMBLE_CPP_LOG_OVERRIDE_COLOR_DEBUG_NONE
bool "None"
config NIMBLE_CPP_LOG_OVERRIDE_COLOR_DEBUG_BLACK
bool "Black"
config NIMBLE_CPP_LOG_OVERRIDE_COLOR_DEBUG_RED
bool "Red"
config NIMBLE_CPP_LOG_OVERRIDE_COLOR_DEBUG_GREEN
bool "Green"
config NIMBLE_CPP_LOG_OVERRIDE_COLOR_DEBUG_YELLOW
bool "Yellow"
config NIMBLE_CPP_LOG_OVERRIDE_COLOR_DEBUG_BLUE
bool "Blue"
config NIMBLE_CPP_LOG_OVERRIDE_COLOR_DEBUG_PURPLE
bool "Purple"
config NIMBLE_CPP_LOG_OVERRIDE_COLOR_DEBUG_CYAN
bool "Cyan"
endchoice #NIMBLE_CPP_LOG_OVERRIDE_COLOR_DEBUG
endmenu
config NIMBLE_CPP_ENABLE_RETURN_CODE_TEXT config NIMBLE_CPP_ENABLE_RETURN_CODE_TEXT
bool "Show NimBLE return codes as text in debug log." bool "Show NimBLE return codes as text in debug log."
default "n" default "n"
@ -50,6 +157,20 @@ config NIMBLE_CPP_ENABLE_ADVERTISEMENT_TYPE_TEXT
while scanning as text messages in the debug log. while scanning as text messages in the debug log.
This will use approximately 250 bytes of flash memory. This will use approximately 250 bytes of flash memory.
config NIMBLE_CPP_ADDR_FMT_EXCLUDE_DELIMITER
bool "Exclude colon characters when printing address."
default "n"
help
Enabling this option will format MAC addresses without
colon characters when printing.
config NIMBLE_CPP_ADDR_FMT_UPPERCASE
bool "Use uppercase letters when printing address."
default "n"
help
Enabling this option will format MAC addresses in
uppercase letters when printing.
config NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED config NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED
bool "Enable timestamps to be stored with attribute values." bool "Enable timestamps to be stored with attribute values."
default "n" default "n"
@ -76,4 +197,58 @@ config NIMBLE_CPP_DEBUG_ASSERT_ENABLED
Enabling this option will add debug asserts to the NimBLE CPP library. Enabling this option will add debug asserts to the NimBLE CPP library.
This will use approximately 1kB of flash memory. This will use approximately 1kB of flash memory.
endmenu config NIMBLE_CPP_FREERTOS_TASK_BLOCK_BIT
int "FreeRTOS task block bit."
default 31
help
Configure the bit to set in the task notification value when a task is blocked waiting for an event.
This should be set to a bit that is not used by other notifications in the system.
#
# BT config
#
config BT_ENABLED
bool "Bluetooth"
default "y"
help
Select this option to enable Bluetooth and show the submenu with Bluetooth configuration choices.
config BT_NIMBLE_ENABLED
bool "NimBLE - BLE only"
default "y"
help
This option is recommended for BLE only usecases to save on memory
if IDF_TARGET_ESP32P4
config BT_NIMBLE_TRANSPORT_UART
bool "Enable Uart Transport"
default "n"
#
# Enable ESP Hosted BT
# Used as VHCI transport between BT Host and Controller
#
config ESP_ENABLE_BT
bool "Enable Hosted Bluetooth support"
default "y"
help
Enable Bluetooth Support via Hosted
choice ESP_WIFI_REMOTE_LIBRARY
prompt "Choose WiFi-remote implementation"
default ESP_WIFI_REMOTE_LIBRARY_HOSTED
help
Select type of WiFi Remote implementation
ESP-HOSTED is the default and most versatile option.
It's also possible to use EPPP, which uses PPPoS link between micros and NAPT, so it's slower
and less universal.
config ESP_WIFI_REMOTE_LIBRARY_HOSTED
bool "ESP-HOSTED"
endchoice
endif
endmenu

View File

@ -186,7 +186,7 @@
same "printed page" as the copyright notice for easier same "printed page" as the copyright notice for easier
identification within third-party archives. identification within third-party archives.
Copyright {2020} {Ryan Powell} Copyright {yyyy} {name of copyright owner}
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -199,5 +199,3 @@
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
This product partly derives from esp32-snippets; Copyright 2017 Neil Kolban.

View File

@ -0,0 +1,10 @@
esp-nimble-cpp
NimBLE-Arduino
Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
esp-nimble-cpp, NimBLE-Arduino contributors.
The Initial Developer of some parts of this library, which are copied from,
derived from, or inspired by is, esp32-snippets, Copyright 2017 Neil Kolban.
If this library is used for commercial purposes, it is requested that the user consider
making a donation and/or sponsoring this project to support it's ongoing development.

View File

@ -1,19 +1,20 @@
[Latest release ![Release Version](https://img.shields.io/github/release/h2zero/esp-nimble-cpp.svg?style=plastic) [![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/) ![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) > [!IMPORTANT]
<br/> > Version 2 is now released!
> Check out the [1.x to 2.x Migration Guide](docs/1.x_to2.x_migration_guide.md) and [Release Notes](https://github.com/h2zero/esp-nimble-cpp/releases/latest/)
# esp-nimble-cpp # esp-nimble-cpp
NimBLE CPP library for use with ESP32 that attempts to maintain compatibility with the [nkolban cpp_uitls BLE API](https://github.com/nkolban/esp32-snippets/tree/master/cpp_utils). NimBLE CPP library for use with ESP32 that attempts to maintain compatibility with the [nkolban cpp_utils BLE API](https://github.com/nkolban/esp32-snippets/tree/master/cpp_utils).
**An Arduino version of this library, including NimBLE, can be [found here.](https://github.com/h2zero/NimBLE-Arduino)** **An Arduino version of this library, including NimBLE, can be [found here.](https://github.com/h2zero/NimBLE-Arduino)**
This library **significantly** reduces resource usage and improves performance for ESP32 BLE applications as compared This library **significantly** reduces resource usage and improves performance for ESP32 BLE applications as compared
with the bluedroid based library. The goal is to maintain, as much as reasonable, compatibility with the original with the bluedroid based library. The goal is to maintain, as much as reasonable, compatibility with the original
library but refactored to use the NimBLE stack. In addition, this library will be more actively developed and maintained library but using the NimBLE stack. In addition, this library will be more actively developed and maintained
to provide improved capabilites and stability over the original. to provide improved capabilities and stability over the original.
**Testing shows a nearly 50% reduction in flash use and approx. 100kB less ram consumed vs the original!** **Testing shows a nearly 50% reduction in flash use and approx. 100kB less ram consumed vs the original!**
*Your results may vary* *Your results may vary*
@ -35,16 +36,6 @@ Configure settings in `NimBLE Options`.
Call `NimBLEDevice::init("");` in `app_main`. Call `NimBLEDevice::init("");` in `app_main`.
<br/> <br/>
### ESP-IDF v3.2 & v3.3
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`.
<br/>
# Using # Using
This library is intended to be compatible with the original ESP32 BLE functions and types with minor changes. This library is intended to be compatible with the original ESP32 BLE functions and types with minor changes.
@ -62,6 +53,14 @@ When using this library along with Arduino and compiling with *CMake* you must a
in your project/CMakeLists.txt after the line `include($ENV{IDF_PATH}/tools/cmake/project.cmake)` to prevent Arduino from releasing BLE memory. in your project/CMakeLists.txt after the line `include($ENV{IDF_PATH}/tools/cmake/project.cmake)` to prevent Arduino from releasing BLE memory.
<br> <br>
# Sponsors
Thank you to all the sponsors who support this project!
<!-- sponsors --><!-- sponsors -->
If you use this library for a commercial product please consider [sponsoring the development](https://github.com/sponsors/h2zero) to ensure the continued updates and maintenance.
<br/>
# Acknowledgments # 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. * [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. * [beegee-tokyo](https://github.com/beegee-tokyo) for contributing your time to test/debug and contributing the beacon examples.

View File

@ -1,2 +0,0 @@
COMPONENT_ADD_INCLUDEDIRS := src
COMPONENT_SRCDIRS := src

View File

@ -0,0 +1,166 @@
# Migrating from 1.x to 2.x
Nearly all of the codebase has been updated and changed under the surface, which has greatly reduced the resource use of the library and improved it's performance. To be able to support these changes it required various API changes and additions.
This guide will help you migrate your application code to use the new API.
The changes listed here are only the required changes that must be made, and a short overview of options for migrating existing applications.
* [General changes](#general-changes)
* [BLE Device](#ble-device)
* [BLE Addresses](#ble-addresses)
* [BLE UUID's](#ble-uuids)
* [Server](#server)
* [Services](#services)
* [Characteristics](#characteristics)
* [Characteristic Callbacks](#characteristic-callbacks)
* [Security](#server)
* [Client](#client)
* [Client Callbacks](#client-callbacks)
* [Remote Services](#remote-services)
* [Remote characteristics](#remote-characteristics)
* [Scanning](#scan)
* [Advertise device](#advertised-device)
* [Advertising](#advertising)
* [Beacons](#beacons)
* [Utilities](#utilities)
<br/>
## General changes
- All functions that take a time parameter now require the time in milliseconds instead of seconds, i.e. `NimBLEScan::start(10000); // 10 seconds`
- `NimBLESecurity` class has been removed it's functionality has been merged into the `NimBLEServer` and `NimBLEClient` classes.
- All connection oriented callbacks now receive a reference to `NimBLEConnInfo` and the `ble_gap_conn_desc` parameter has been replaced with `NimBLEConnInfo` in the functions that received it.
For instance `onAuthenticationComplete(ble_gap_conn_desc* desc)` signature is now `onAuthenticationComplete(NimBLEConnInfo& connInfo)` and
`NimBLEServerCallbacks::onConnect(NimBLEServer* pServer)` signature is now `NimBLEServerCallbacks::onConnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo)`.
<br/>
## BLE Device
- Ignore list functions and vector have been removed, the application should implement this if desired. It was no longer used by the library.
- `NimBLEDevice::startSecurity` now returns a `bool`, true on success, instead of an int to be consistent with the rest of the library.
- `NimBLEDevice::getInitialized` renamed to `NimBLEDevice::isInitialized`.
- `NimBLEDevice::setPower` no longer takes the `esp_power_level_t` and `esp_ble_power_type_t`, instead only an integer value in dbm units is accepted, so instead of `ESP_PWR_LVL_P9` an `int8_t` value of `9` would be used for the same result.
- `NimBLEDevice::setOwnAddrType` no longer takes a `bool nrpa` parameter, the random address type will be determined by the bits the in the address instead.
Note: If setting a custom address, it should be set with `NimBLEDevice::setOwnAddr` first before calling `NimBLEDevice::setOwnAddrType`.
- `NimBLEDevice::getClientListSize` replaced with `NimBLEDevice::getCreatedClientCount`.
- `NimBLEDevice::getClientList` was removed and `NimBLEDevice::getConnectedClients` can be used instead which returns a `std::vector` of pointers to the connected client instances. This was done because internally the clients are managed in a `std::array` which replaced the 'std::list`.
<br/>
## BLE Addresses
NimBLEAddress comparisons have changed to now consider the address type. If 2 address values are the same but the type is different then they are no longer considered equal. This is a correction to the 1.x version which did not consider the type, whereas the BLE specification states:
> Whenever two device addresses are compared, the comparison shall include the device address type (i.e. if the two addresses have different types, they are different even if the two 48-bit addresses are the same).
This means that if in your application you create a NimBLEAddress instance and are comparing a scan result or some other address created by the library and you did not define the address type then the comparison may fail.
The default address type is public `0`, whereas many devices use a random `1` address type.
If you experience this issue please create your address instances with the address type specified, i.e. `NimBLEAddress address("11:22:33:44:55:66", TYPE_HERE)`
`NimBLEAddress::getNative` has been removed and replaced with `NimBLEAddress::getBase`.
This returns a pointer to `const ble_addr_t` instead of a pointer to the address value that `getNative` did. The value can be accessed through this struct via `ble_addr_t.value` and type is in `ble_addr_t.type`.
<br/>
## BLE UUID's
- `NimBLEUUID::getNative` method replaced with `NimBLEUUID::getBase` which returns a read-only pointer to the underlying `ble_uuid_t` struct.
- `NimBLEUUID`; `msbFirst` parameter has been removed from constructor, caller should reverse the data first or call the new `NimBLEUUID::reverseByteOrder` method after.
<br/>
## Server
- `NimBLEServer::disconnect` now returns `bool`, true = success, instead of `int` to be consistent with the rest of the library.
- `NimBLEServerCallbacks::onMTUChanged` renamed to `NimBLEServerCallbacks::onMTUChange` to be consistent with the client callback.
- `NimBLEServer::getPeerIDInfo` renamed to `NimBLEServer::getPeerInfoByHandle` to better describe it's use.
- Advertising is no longer automatically restarted when a peer disconnects, to re-enable this feature either call `NimBLEServer::advertiseOnDisconnect(true);` after creating the server or manually restart advertising in the `onDisconnect` callback.
<br/>
### Services
- `NimBLEService::getCharacteristics` now returns a `const std::vector<NimBLECharacteristic*>&` instead of a copy of the vector.
<br/>
### Characteristics
- `NimBLECharacteristic::notify` no longer takes a `bool is_notification` parameter, instead `NimBLECharacteristic::indicate` should be called to send indications.
<br/>
#### Characteristic callbacks
- `NimBLECharacteristicCallbacks::onNotify` removed as unnecessary, the library does not call notify without app input.
- `NimBLECharacteristicCallbacks::onStatus` No longer takes a `status` parameter, refer to the return code parameter for success/failure.
<br/>
### Server Security
- `NimBLEServerCallbacks::onPassKeyRequest` has been renamed to `NimBLEServerCallbacks::onPassKeyDisplay` as it is intended that the device should display the passkey for the client to enter.
- `NimBLEServerCallbacks::onAuthenticationComplete` now takes a `NimBLEConnInfo&` parameter.
<br/>
## Client
- `NimBLEClient::getServices` now returns a const reference to std::vector<NimBLERemoteService*> instead of a pointer to the internal vector.
- `NimBLEClient::getConnId` has been renamed to `getConnHandle` to be consistent with bluetooth terminology.
- `NimBLEClient::disconnect` now returns a `bool`, true on success, instead of an `int` to be consistent with the rest of the library.
<br/>
### Client callbacks
- `NimBLEClientCallbacks::onConfirmPIN` renamed to `NimBLEClientCallbacks::onConfirmPasskey`, takes a `NimBLEConnInfo&` parameter and no longer returns a value. This should be used to prompt a user to confirm the pin on the display (YES/NO) after which the response should be sent with `NimBLEDevice::injectConfirmPasskey`.
- `NimBLEClientCallbacks::onPassKeyRequest` has been changed to `NimBLEClientCallbacks::onPassKeyEntry` which takes a `NimBLEConnInfo&` parameter and no longer returns a value. Instead of returning a value this callback should prompt a user to enter a passkey number which is sent later via `NimBLEDevice::injectPassKey`.
<br/>
### Remote Services
- `NimBLERemoteService::getCharacteristics` now returns a `const std::vector<NimBLERemoteCharacteristic*>&` instead of non-const `std::vector<NimBLERemoteCharacteristic*>*`.
<br/>
### Remote Characteristics
- `NimBLERemoteCharacteristic::getRemoteService` now returns a `const NimBLERemoteService*` instead of non-const.
- `NimBLERemoteCharacteristic::registerForNotify`, has been removed, the application should use `NimBLERemoteCharacteristic::subscribe` and `NimBLERemoteCharacteristic::unSubscribe`.
`NimBLERemoteCharacteristic::readUInt32`
`NimBLERemoteCharacteristic::readUInt16`
`NimBLERemoteCharacteristic::readUInt8`
`NimBLERemoteCharacteristic::readFloat`
Have been removed, instead the application should use `NimBLERemoteCharacteristic::readValue<type\>`.
<br/>
## Scan
- `NimBLEScan::stop` will no longer call the `onScanEnd` callback as the caller should know it has been stopped either by initiating a connection or calling this function itself.
- `NimBLEScan::clearDuplicateCache` has been removed as it was problematic and only for the original esp32. The application should stop and start the scanner for the same effect or call `NimBLEScan::start` with the new `bool restart` parameter set to true.
- `NimBLEScanResults::getDevice` methods now return `const NimBLEAdvertisedDevice*` instead of a non-const pointer.
- `NimBLEScanResults` iterators are now `const_iterator`.
- The callback parameter for `NimBLEScan::start` has been removed and the blocking overload of `NimBLEScan::start` has been replaced by an overload of `NimBLEScan::getResults` with the same parameters.
So if your code prior was this:
NimBLEScanResults results = pScan->start(10, false);
It should now be:
NimBLEScanResults results = pScan->getResults(10000, false); // note the time is now in milliseconds
- `NimBLEAdvertisedDeviceCallbacks` Has been replaced by `NimBLEScanCallbacks` which contains the following methods:
- - `NimBLEScanCallbacks::onResult`, functions the same as the old `NimBLEAdvertisedDeviceCallbacks::onResult` but now takes aa `const NimBLEAdvertisedDevice*` instead of non-const.
- - `NimBLEScanCallbacks::onScanEnd`, replaces the scanEnded callback passed to `NimBLEScan::start` and now takes a `const NimBLEScanResults&` and `int reason` parameter.
- - `NimBLEScanCallbacks::onDiscovered`, This is called immediately when a device is first scanned, before any scan response data is available and takes a `const NimBLEAdvertisedDevice*` parameter.
<br/>
### Advertised Device
- `NimBLEAdvertisedDevice::hasRSSI` removed as redundant, RSSI is always available.
- `NimBLEAdvertisedDevice::getPayload` now returns `const std::vector<uint8_t>&` instead of a pointer to internal memory.
- `NimBLEAdvertisedDevice` Timestamp removed, if needed then the app should track the time from the callback.
<br/>
## Advertising
- `NimBLEAdvertising::setMinPreferred` and `NimBLEAdvertising::setMaxPreferred` have been removed and replaced by `NimBLEAdvertising::setPreferredParams` which takes both the min and max parameters.
- Advertising the name and TX power of the device will no longer happen by default and should be set manually by the application using `NimBLEAdvertising::setName` and `NimBLEAdvertising::addTxPower`.
- `NimBLEAdvertising::setAdvertisementType` has been renamed to `NimBLEAdvertising::setConnectableMode` to better reflect it's function.
- `NimBLEAdvertising::setScanResponse` has been renamed to `NimBLEAdvertising::enableScanResponse` to better reflect it's function.
- `NimBLEAdvertising`; Scan response is no longer enabled by default.
- `NimBLEAdvertising::start` No longer takes a callback pointer parameter, instead the new method `NimBLEAdvertising::setAdvertisingCompleteCallback` should be used to set the callback function.
<br/>
## Beacons
- Removed Eddystone URL as it has been shutdown by google since 2021.
- `NimBLEEddystoneTLM::setTemp` now takes an `int16_t` parameter instead of float to be friendly to devices without floating point support.
- `NimBLEEddystoneTLM::getTemp` now returns `int16_t` to work with devices that don't have floating point support.
- `NimBLEEddystoneTLM::setData` now takes a reference to * `NimBLEEddystoneTLM::BeaconData` instead of `std::string`.
- `NimBLEEddystoneTLM::getData` now returns a reference to * `NimBLEEddystoneTLM::BeaconData` instead of `std::string`.
- `NimBLEBeacon::setData` now takes `const NimBLEBeacon::BeaconData&` instead of `std::string`.
- `NimBLEBeacon::getData` now returns `const NimBLEBeacon::BeaconData&` instead of `std::string`.
<br/>
## Utilities
- `NimBLEUtils::dumpGapEvent` function removed.
- `NimBLEUtils::buildHexData` replaced with `NimBLEUtils::dataToHexString`, which returns a `std::string` containing the hex string.
<br/>

View File

@ -1,4 +1,4 @@
# Doxyfile 1.9.5 # Doxyfile 1.10.0
# This file describes the settings to be used by the documentation system # This file describes the settings to be used by the documentation system
# doxygen (www.doxygen.org) for a project. # doxygen (www.doxygen.org) for a project.
@ -48,7 +48,7 @@ PROJECT_NAME = esp-nimble-cpp
# could be handy for archiving the generated documentation or if some version # could be handy for archiving the generated documentation or if some version
# control system is used. # control system is used.
PROJECT_NUMBER = 1.4.1 PROJECT_NUMBER = 2.3.0
# Using the PROJECT_BRIEF tag one can provide an optional one line description # 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 # for a project that appears at the top of each page and should give viewer a
@ -63,6 +63,12 @@ PROJECT_BRIEF =
PROJECT_LOGO = PROJECT_LOGO =
# With the PROJECT_ICON tag one can specify an icon that is included in the tabs
# when the HTML document is shown. Doxygen will copy the logo to the output
# directory.
PROJECT_ICON =
# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path
# into which the generated documentation will be written. If a relative path is # into which the generated documentation will be written. If a relative path is
# entered, it will be relative to the location where doxygen was started. If # entered, it will be relative to the location where doxygen was started. If
@ -86,7 +92,7 @@ CREATE_SUBDIRS = NO
# level increment doubles the number of directories, resulting in 4096 # level increment doubles the number of directories, resulting in 4096
# directories at level 8 which is the default and also the maximum value. The # directories at level 8 which is the default and also the maximum value. The
# sub-directories are organized in 2 levels, the first level always has a fixed # sub-directories are organized in 2 levels, the first level always has a fixed
# numer of 16 directories. # number of 16 directories.
# Minimum value: 0, maximum value: 8, default value: 8. # Minimum value: 0, maximum value: 8, default value: 8.
# This tag requires that the tag CREATE_SUBDIRS is set to YES. # This tag requires that the tag CREATE_SUBDIRS is set to YES.
@ -363,6 +369,17 @@ MARKDOWN_SUPPORT = YES
TOC_INCLUDE_HEADINGS = 5 TOC_INCLUDE_HEADINGS = 5
# The MARKDOWN_ID_STYLE tag can be used to specify the algorithm used to
# generate identifiers for the Markdown headings. Note: Every identifier is
# unique.
# Possible values are: DOXYGEN use a fixed 'autotoc_md' string followed by a
# sequence number starting at 0 and GITHUB use the lower case version of title
# with any whitespace replaced by '-' and punctuation characters removed.
# The default value is: DOXYGEN.
# This tag requires that the tag MARKDOWN_SUPPORT is set to YES.
MARKDOWN_ID_STYLE = GITHUB
# When enabled doxygen tries to link words that correspond to documented # When enabled doxygen tries to link words that correspond to documented
# classes, or namespaces to their corresponding documentation. Such a link can # classes, or namespaces to their corresponding documentation. Such a link can
# be prevented in individual cases by putting a % sign in front of the word or # be prevented in individual cases by putting a % sign in front of the word or
@ -487,6 +504,14 @@ LOOKUP_CACHE_SIZE = 0
NUM_PROC_THREADS = 1 NUM_PROC_THREADS = 1
# If the TIMESTAMP tag is set different from NO then each generated page will
# contain the date or date and time when the page was generated. Setting this to
# NO can help when comparing the output of multiple runs.
# Possible values are: YES, NO, DATETIME and DATE.
# The default value is: NO.
TIMESTAMP = NO
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
# Build related configuration options # Build related configuration options
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
@ -568,7 +593,8 @@ HIDE_UNDOC_MEMBERS = YES
# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all # If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all
# undocumented classes that are normally visible in the class hierarchy. If set # undocumented classes that are normally visible in the class hierarchy. If set
# to NO, these classes will be included in the various overviews. This option # to NO, these classes will be included in the various overviews. This option
# has no effect if EXTRACT_ALL is enabled. # will also hide undocumented C++ concepts if enabled. This option has no effect
# if EXTRACT_ALL is enabled.
# The default value is: NO. # The default value is: NO.
HIDE_UNDOC_CLASSES = YES HIDE_UNDOC_CLASSES = YES
@ -859,14 +885,29 @@ WARN_IF_INCOMPLETE_DOC = YES
WARN_NO_PARAMDOC = NO WARN_NO_PARAMDOC = NO
# If WARN_IF_UNDOC_ENUM_VAL option is set to YES, doxygen will warn about
# undocumented enumeration values. If set to NO, doxygen will accept
# undocumented enumeration values. If EXTRACT_ALL is set to YES then this flag
# will automatically be disabled.
# The default value is: NO.
WARN_IF_UNDOC_ENUM_VAL = NO
# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when # If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when
# a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS # a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS
# then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but # then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but
# at the end of the doxygen process doxygen will return with a non-zero status. # at the end of the doxygen process doxygen will return with a non-zero status.
# Possible values are: NO, YES and FAIL_ON_WARNINGS. # If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS_PRINT then doxygen behaves
# like FAIL_ON_WARNINGS but in case no WARN_LOGFILE is defined doxygen will not
# write the warning messages in between other messages but write them at the end
# of a run, in case a WARN_LOGFILE is defined the warning messages will be
# besides being in the defined file also be shown at the end of a run, unless
# the WARN_LOGFILE is defined as - i.e. standard output (stdout) in that case
# the behavior will remain as with the setting FAIL_ON_WARNINGS.
# Possible values are: NO, YES, FAIL_ON_WARNINGS and FAIL_ON_WARNINGS_PRINT.
# The default value is: NO. # The default value is: NO.
WARN_AS_ERROR = YES WARN_AS_ERROR = NO
# The WARN_FORMAT tag determines the format of the warning messages that doxygen # The WARN_FORMAT tag determines the format of the warning messages that doxygen
# can produce. The string should contain the $file, $line, and $text tags, which # can produce. The string should contain the $file, $line, and $text tags, which
@ -943,12 +984,12 @@ INPUT_FILE_ENCODING =
# Note the list of default checked file patterns might differ from the list of # Note the list of default checked file patterns might differ from the list of
# default file extension mappings. # default file extension mappings.
# #
# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, # If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cxxm,
# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, # *.cpp, *.cppm, *.ccm, *.c++, *.c++m, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl,
# *.hh, *.hxx, *.hpp, *.h++, *.l, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, # *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, *.h++, *.ixx, *.l, *.cs, *.d,
# *.inc, *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C # *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, *.md, *.mm, *.dox (to
# comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd, # be provided as doxygen C comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08,
# *.vhdl, *.ucf, *.qsf and *.ice. # *.f18, *.f, *.for, *.vhd, *.vhdl, *.ucf, *.qsf and *.ice.
FILE_PATTERNS = *.c \ FILE_PATTERNS = *.c \
*.cc \ *.cc \
@ -1034,9 +1075,6 @@ EXCLUDE_PATTERNS =
# output. The symbol name can be a fully qualified name, a word, or if the # output. The symbol name can be a fully qualified name, a word, or if the
# wildcard * is used, a substring. Examples: ANamespace, AClass, # wildcard * is used, a substring. Examples: ANamespace, AClass,
# ANamespace::AClass, ANamespace::*Test # ANamespace::AClass, ANamespace::*Test
#
# Note that the wildcards are matched against the file with absolute path, so to
# exclude all test directories use the pattern */test/*
EXCLUDE_SYMBOLS = EXCLUDE_SYMBOLS =
@ -1150,7 +1188,8 @@ FORTRAN_COMMENT_AFTER = 72
SOURCE_BROWSER = NO SOURCE_BROWSER = NO
# Setting the INLINE_SOURCES tag to YES will include the body of functions, # Setting the INLINE_SOURCES tag to YES will include the body of functions,
# classes and enums directly into the documentation. # multi-line macros, enums or list initialized variables directly into the
# documentation.
# The default value is: NO. # The default value is: NO.
INLINE_SOURCES = NO INLINE_SOURCES = NO
@ -1222,46 +1261,6 @@ USE_HTAGS = NO
VERBATIM_HEADERS = YES VERBATIM_HEADERS = YES
# If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the
# clang parser (see:
# http://clang.llvm.org/) for more accurate parsing at the cost of reduced
# performance. This can be particularly helpful with template rich C++ code for
# which doxygen's built-in parser lacks the necessary type information.
# Note: The availability of this option depends on whether or not doxygen was
# generated with the -Duse_libclang=ON option for CMake.
# The default value is: NO.
CLANG_ASSISTED_PARSING = NO
# If the CLANG_ASSISTED_PARSING tag is set to YES and the CLANG_ADD_INC_PATHS
# tag is set to YES then doxygen will add the directory of each input to the
# include path.
# The default value is: YES.
# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES.
CLANG_ADD_INC_PATHS = YES
# If clang assisted parsing is enabled you can provide the compiler with command
# line options that you would normally use when invoking the compiler. Note that
# the include paths will already be set by doxygen for the files and directories
# specified with INPUT and INCLUDE_PATH.
# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES.
CLANG_OPTIONS =
# If clang assisted parsing is enabled you can provide the clang parser with the
# path to the directory containing a file called compile_commands.json. This
# file is the compilation database (see:
# http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html) containing the
# options used when the source files were built. This is equivalent to
# specifying the -p option to a clang tool, such as clang-check. These options
# will then be passed to the parser. Any options specified with CLANG_OPTIONS
# will be added as well.
# Note: The availability of this option depends on whether or not doxygen was
# generated with the -Duse_libclang=ON option for CMake.
CLANG_DATABASE_PATH =
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
# Configuration options related to the alphabetical class index # Configuration options related to the alphabetical class index
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
@ -1273,10 +1272,11 @@ CLANG_DATABASE_PATH =
ALPHABETICAL_INDEX = YES ALPHABETICAL_INDEX = YES
# In case all classes in a project start with a common prefix, all classes will # The IGNORE_PREFIX tag can be used to specify a prefix (or a list of prefixes)
# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag # that should be ignored while generating the index headers. The IGNORE_PREFIX
# can be used to specify a prefix (or a list of prefixes) that should be ignored # tag works for classes, function and member names. The entity will be placed in
# while generating the index headers. # the alphabetical list under the first letter of the entity name that remains
# after removing the prefix.
# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. # This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
IGNORE_PREFIX = IGNORE_PREFIX =
@ -1355,7 +1355,12 @@ HTML_STYLESHEET =
# Doxygen will copy the style sheet files to the output directory. # Doxygen will copy the style sheet files to the output directory.
# Note: The order of the extra style sheet files is of importance (e.g. the last # Note: The order of the extra style sheet files is of importance (e.g. the last
# style sheet in the list overrules the setting of the previous ones in the # style sheet in the list overrules the setting of the previous ones in the
# list). For an example see the documentation. # list).
# Note: Since the styling of scrollbars can currently not be overruled in
# Webkit/Chromium, the styling will be left out of the default doxygen.css if
# one or more extra stylesheets have been specified. So if scrollbar
# customization is desired it has to be added explicitly. For an example see the
# documentation.
# This tag requires that the tag GENERATE_HTML is set to YES. # This tag requires that the tag GENERATE_HTML is set to YES.
HTML_EXTRA_STYLESHEET = HTML_EXTRA_STYLESHEET =
@ -1371,17 +1376,13 @@ HTML_EXTRA_STYLESHEET =
HTML_EXTRA_FILES = HTML_EXTRA_FILES =
# The HTML_COLORSTYLE tag can be used to specify if the generated HTML output # The HTML_COLORSTYLE tag can be used to specify if the generated HTML output
# should be rendered with a dark or light theme. Default setting AUTO_LIGHT # should be rendered with a dark or light theme.
# enables light output unless the user preference is dark output. Other options # Possible values are: LIGHT always generate light mode output, DARK always
# are DARK to always use dark mode, LIGHT to always use light mode, AUTO_DARK to # generate dark mode output, AUTO_LIGHT automatically set the mode according to
# default to dark mode unless the user prefers light mode, and TOGGLE to let the # the user preference, use light mode if no preference is set (the default),
# user toggle between dark and light mode via a button. # AUTO_DARK automatically set the mode according to the user preference, use
# Possible values are: LIGHT Always generate light output., DARK Always generate # dark mode if no preference is set and TOGGLE allow to user to switch between
# dark output., AUTO_LIGHT Automatically set the mode according to the user # light and dark mode via a button.
# preference, use light mode if no preference is set (the default)., AUTO_DARK
# Automatically set the mode according to the user preference, use dark mode if
# no preference is set. and TOGGLE Allow to user to switch between light and
# dark mode via a button..
# The default value is: AUTO_LIGHT. # The default value is: AUTO_LIGHT.
# This tag requires that the tag GENERATE_HTML is set to YES. # This tag requires that the tag GENERATE_HTML is set to YES.
@ -1417,15 +1418,6 @@ HTML_COLORSTYLE_SAT = 100
HTML_COLORSTYLE_GAMMA = 80 HTML_COLORSTYLE_GAMMA = 80
# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
# page will contain the date and time when the page was generated. Setting this
# to YES can help to show when doxygen was last run and thus if the
# documentation is up to date.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_TIMESTAMP = NO
# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML # If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML
# documentation will contain a main index with vertical navigation menus that # documentation will contain a main index with vertical navigation menus that
# are dynamically created via JavaScript. If disabled, the navigation index will # are dynamically created via JavaScript. If disabled, the navigation index will
@ -1445,6 +1437,33 @@ HTML_DYNAMIC_MENUS = YES
HTML_DYNAMIC_SECTIONS = NO HTML_DYNAMIC_SECTIONS = NO
# If the HTML_CODE_FOLDING tag is set to YES then classes and functions can be
# dynamically folded and expanded in the generated HTML source code.
# The default value is: YES.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_CODE_FOLDING = YES
# If the HTML_COPY_CLIPBOARD tag is set to YES then doxygen will show an icon in
# the top right corner of code and text fragments that allows the user to copy
# its content to the clipboard. Note this only works if supported by the browser
# and the web page is served via a secure context (see:
# https://www.w3.org/TR/secure-contexts/), i.e. using the https: or file:
# protocol.
# The default value is: YES.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_COPY_CLIPBOARD = YES
# Doxygen stores a couple of settings persistently in the browser (via e.g.
# cookies). By default these settings apply to all HTML pages generated by
# doxygen across all projects. The HTML_PROJECT_COOKIE tag can be used to store
# the settings under a project specific key, such that the user preferences will
# be stored separately.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_PROJECT_COOKIE =
# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries # With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries
# shown in the various tree structured indices initially; the user can expand # shown in the various tree structured indices initially; the user can expand
# and collapse entries dynamically later on. Doxygen will expand the tree to # and collapse entries dynamically later on. Doxygen will expand the tree to
@ -1575,6 +1594,16 @@ BINARY_TOC = NO
TOC_EXPAND = NO TOC_EXPAND = NO
# The SITEMAP_URL tag is used to specify the full URL of the place where the
# generated documentation will be placed on the server by the user during the
# deployment of the documentation. The generated sitemap is called sitemap.xml
# and placed on the directory specified by HTML_OUTPUT. In case no SITEMAP_URL
# is specified no sitemap is generated. For information about the sitemap
# protocol see https://www.sitemaps.org
# This tag requires that the tag GENERATE_HTML is set to YES.
SITEMAP_URL =
# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that # QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that
# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help # can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help
@ -1811,8 +1840,8 @@ MATHJAX_RELPATH = https://cdn.jsdelivr.net/npm/mathjax@2
# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax # The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax
# extension names that should be enabled during MathJax rendering. For example # extension names that should be enabled during MathJax rendering. For example
# for MathJax version 2 (see https://docs.mathjax.org/en/v2.7-latest/tex.html # for MathJax version 2 (see
# #tex-and-latex-extensions): # https://docs.mathjax.org/en/v2.7-latest/tex.html#tex-and-latex-extensions):
# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols # MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols
# For example for MathJax version 3 (see # For example for MathJax version 3 (see
# http://docs.mathjax.org/en/latest/input/tex/extensions/index.html): # http://docs.mathjax.org/en/latest/input/tex/extensions/index.html):
@ -2063,9 +2092,16 @@ PDF_HYPERLINKS = YES
USE_PDFLATEX = YES USE_PDFLATEX = YES
# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode # The LATEX_BATCHMODE tag signals the behavior of LaTeX in case of an error.
# command to the generated LaTeX files. This will instruct LaTeX to keep running # Possible values are: NO same as ERROR_STOP, YES same as BATCH, BATCH In batch
# if errors occur, instead of asking the user for help. # mode nothing is printed on the terminal, errors are scrolled as if <return> is
# hit at every error; missing files that TeX tries to input or request from
# keyboard input (\read on a not open input stream) cause the job to abort,
# NON_STOP In nonstop mode the diagnostic message will appear on the terminal,
# but there is no possibility of user interaction just like in batch mode,
# SCROLL In scroll mode, TeX will stop only for missing files to input or if
# keyboard input is necessary and ERROR_STOP In errorstop mode, TeX will stop at
# each error, asking for user intervention.
# The default value is: NO. # The default value is: NO.
# This tag requires that the tag GENERATE_LATEX is set to YES. # This tag requires that the tag GENERATE_LATEX is set to YES.
@ -2086,14 +2122,6 @@ LATEX_HIDE_INDICES = NO
LATEX_BIB_STYLE = plain LATEX_BIB_STYLE = plain
# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated
# page will contain the date and time when the page was generated. Setting this
# to NO can help when comparing the output of multiple runs.
# The default value is: NO.
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_TIMESTAMP = NO
# The LATEX_EMOJI_DIRECTORY tag is used to specify the (relative or absolute) # The LATEX_EMOJI_DIRECTORY tag is used to specify the (relative or absolute)
# path from which the emoji images will be read. If a relative path is entered, # path from which the emoji images will be read. If a relative path is entered,
# it will be relative to the LATEX_OUTPUT directory. If left blank the # it will be relative to the LATEX_OUTPUT directory. If left blank the
@ -2259,7 +2287,7 @@ DOCBOOK_OUTPUT = docbook
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an # If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an
# AutoGen Definitions (see http://autogen.sourceforge.net/) file that captures # AutoGen Definitions (see https://autogen.sourceforge.net/) file that captures
# the structure of the code including all documentation. Note that this feature # the structure of the code including all documentation. Note that this feature
# is still experimental and incomplete at the moment. # is still experimental and incomplete at the moment.
# The default value is: NO. # The default value is: NO.
@ -2270,6 +2298,28 @@ GENERATE_AUTOGEN_DEF = NO
# Configuration options related to Sqlite3 output # Configuration options related to Sqlite3 output
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
# If the GENERATE_SQLITE3 tag is set to YES doxygen will generate a Sqlite3
# database with symbols found by doxygen stored in tables.
# The default value is: NO.
GENERATE_SQLITE3 = NO
# The SQLITE3_OUTPUT tag is used to specify where the Sqlite3 database will be
# put. If a relative path is entered the value of OUTPUT_DIRECTORY will be put
# in front of it.
# The default directory is: sqlite3.
# This tag requires that the tag GENERATE_SQLITE3 is set to YES.
SQLITE3_OUTPUT = sqlite3
# The SQLITE3_RECREATE_DB tag is set to YES, the existing doxygen_sqlite3.db
# database file will be recreated with each doxygen run. If set to NO, doxygen
# will warn if a database file is already found and not modify it.
# The default value is: YES.
# This tag requires that the tag GENERATE_SQLITE3 is set to YES.
SQLITE3_RECREATE_DB = YES
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
# Configuration options related to the Perl module output # Configuration options related to the Perl module output
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
@ -2418,15 +2468,15 @@ TAGFILES =
GENERATE_TAGFILE = GENERATE_TAGFILE =
# If the ALLEXTERNALS tag is set to YES, all external class will be listed in # If the ALLEXTERNALS tag is set to YES, all external classes and namespaces
# the class index. If set to NO, only the inherited external classes will be # will be listed in the class and namespace index. If set to NO, only the
# listed. # inherited external classes will be listed.
# The default value is: NO. # The default value is: NO.
ALLEXTERNALS = NO ALLEXTERNALS = NO
# If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed # If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed
# in the modules index. If set to NO, only the current project's groups will be # in the topic index. If set to NO, only the current project's groups will be
# listed. # listed.
# The default value is: YES. # The default value is: YES.
@ -2440,16 +2490,9 @@ EXTERNAL_GROUPS = YES
EXTERNAL_PAGES = YES EXTERNAL_PAGES = YES
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
# Configuration options related to the dot tool # Configuration options related to diagram generator tools
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
# You can include diagrams made with dia in doxygen documentation. Doxygen will
# then run dia to produce the diagram and insert it in the documentation. The
# DIA_PATH tag allows you to specify the directory where the dia binary resides.
# If left empty dia is assumed to be found in the default search path.
DIA_PATH =
# If set to YES the inheritance and collaboration graphs will hide inheritance # If set to YES the inheritance and collaboration graphs will hide inheritance
# and usage relations if the target is undocumented or is not a class. # and usage relations if the target is undocumented or is not a class.
# The default value is: YES. # The default value is: YES.
@ -2458,7 +2501,7 @@ HIDE_UNDOC_RELATIONS = YES
# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
# available from the path. This tool is part of Graphviz (see: # available from the path. This tool is part of Graphviz (see:
# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent # https://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
# Bell Labs. The other options in this section have no effect if this option is # Bell Labs. The other options in this section have no effect if this option is
# set to NO # set to NO
# The default value is: NO. # The default value is: NO.
@ -2511,13 +2554,19 @@ DOT_NODE_ATTR = "shape=box,height=0.2,width=0.4"
DOT_FONTPATH = DOT_FONTPATH =
# If the CLASS_GRAPH tag is set to YES (or GRAPH) then doxygen will generate a # If the CLASS_GRAPH tag is set to YES or GRAPH or BUILTIN then doxygen will
# graph for each documented class showing the direct and indirect inheritance # generate a graph for each documented class showing the direct and indirect
# relations. In case HAVE_DOT is set as well dot will be used to draw the graph, # inheritance relations. In case the CLASS_GRAPH tag is set to YES or GRAPH and
# otherwise the built-in generator will be used. If the CLASS_GRAPH tag is set # HAVE_DOT is enabled as well, then dot will be used to draw the graph. In case
# to TEXT the direct and indirect inheritance relations will be shown as texts / # the CLASS_GRAPH tag is set to YES and HAVE_DOT is disabled or if the
# links. # CLASS_GRAPH tag is set to BUILTIN, then the built-in generator will be used.
# Possible values are: NO, YES, TEXT and GRAPH. # If the CLASS_GRAPH tag is set to TEXT the direct and indirect inheritance
# relations will be shown as texts / links. Explicit enabling an inheritance
# graph or choosing a different representation for an inheritance graph of a
# specific class, can be accomplished by means of the command \inheritancegraph.
# Disabling an inheritance graph can be accomplished by means of the command
# \hideinheritancegraph.
# Possible values are: NO, YES, TEXT, GRAPH and BUILTIN.
# The default value is: YES. # The default value is: YES.
CLASS_GRAPH = TEXT CLASS_GRAPH = TEXT
@ -2525,15 +2574,21 @@ CLASS_GRAPH = TEXT
# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a # If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a
# graph for each documented class showing the direct and indirect implementation # graph for each documented class showing the direct and indirect implementation
# dependencies (inheritance, containment, and class references variables) of the # dependencies (inheritance, containment, and class references variables) of the
# class with other documented classes. # class with other documented classes. Explicit enabling a collaboration graph,
# when COLLABORATION_GRAPH is set to NO, can be accomplished by means of the
# command \collaborationgraph. Disabling a collaboration graph can be
# accomplished by means of the command \hidecollaborationgraph.
# The default value is: YES. # The default value is: YES.
# This tag requires that the tag HAVE_DOT is set to YES. # This tag requires that the tag HAVE_DOT is set to YES.
COLLABORATION_GRAPH = YES COLLABORATION_GRAPH = YES
# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for # If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for
# groups, showing the direct groups dependencies. See also the chapter Grouping # groups, showing the direct groups dependencies. Explicit enabling a group
# in the manual. # dependency graph, when GROUP_GRAPHS is set to NO, can be accomplished by means
# of the command \groupgraph. Disabling a directory graph can be accomplished by
# means of the command \hidegroupgraph. See also the chapter Grouping in the
# manual.
# The default value is: YES. # The default value is: YES.
# This tag requires that the tag HAVE_DOT is set to YES. # This tag requires that the tag HAVE_DOT is set to YES.
@ -2575,8 +2630,8 @@ DOT_UML_DETAILS = NO
# The DOT_WRAP_THRESHOLD tag can be used to set the maximum number of characters # The DOT_WRAP_THRESHOLD tag can be used to set the maximum number of characters
# to display on a single line. If the actual line length exceeds this threshold # to display on a single line. If the actual line length exceeds this threshold
# significantly it will wrapped across multiple lines. Some heuristics are apply # significantly it will be wrapped across multiple lines. Some heuristics are
# to avoid ugly line breaks. # applied to avoid ugly line breaks.
# Minimum value: 0, maximum value: 1000, default value: 17. # Minimum value: 0, maximum value: 1000, default value: 17.
# This tag requires that the tag HAVE_DOT is set to YES. # This tag requires that the tag HAVE_DOT is set to YES.
@ -2593,7 +2648,9 @@ TEMPLATE_RELATIONS = NO
# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to # If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to
# YES then doxygen will generate a graph for each documented file showing the # YES then doxygen will generate a graph for each documented file showing the
# direct and indirect include dependencies of the file with other documented # direct and indirect include dependencies of the file with other documented
# files. # files. Explicit enabling an include graph, when INCLUDE_GRAPH is is set to NO,
# can be accomplished by means of the command \includegraph. Disabling an
# include graph can be accomplished by means of the command \hideincludegraph.
# The default value is: YES. # The default value is: YES.
# This tag requires that the tag HAVE_DOT is set to YES. # This tag requires that the tag HAVE_DOT is set to YES.
@ -2602,7 +2659,10 @@ INCLUDE_GRAPH = YES
# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are # If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are
# set to YES then doxygen will generate a graph for each documented file showing # set to YES then doxygen will generate a graph for each documented file showing
# the direct and indirect include dependencies of the file with other documented # the direct and indirect include dependencies of the file with other documented
# files. # files. Explicit enabling an included by graph, when INCLUDED_BY_GRAPH is set
# to NO, can be accomplished by means of the command \includedbygraph. Disabling
# an included by graph can be accomplished by means of the command
# \hideincludedbygraph.
# The default value is: YES. # The default value is: YES.
# This tag requires that the tag HAVE_DOT is set to YES. # This tag requires that the tag HAVE_DOT is set to YES.
@ -2642,7 +2702,10 @@ GRAPHICAL_HIERARCHY = YES
# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the # If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the
# dependencies a directory has on other directories in a graphical way. The # dependencies a directory has on other directories in a graphical way. The
# dependency relations are determined by the #include relations between the # dependency relations are determined by the #include relations between the
# files in the directories. # files in the directories. Explicit enabling a directory graph, when
# DIRECTORY_GRAPH is set to NO, can be accomplished by means of the command
# \directorygraph. Disabling a directory graph can be accomplished by means of
# the command \hidedirectorygraph.
# The default value is: YES. # The default value is: YES.
# This tag requires that the tag HAVE_DOT is set to YES. # This tag requires that the tag HAVE_DOT is set to YES.
@ -2658,7 +2721,7 @@ DIR_GRAPH_MAX_DEPTH = 1
# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
# generated by dot. For an explanation of the image formats see the section # generated by dot. For an explanation of the image formats see the section
# output formats in the documentation of the dot tool (Graphviz (see: # output formats in the documentation of the dot tool (Graphviz (see:
# http://www.graphviz.org/)). # https://www.graphviz.org/)).
# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order # Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order
# to make the SVG files visible in IE 9+ (other browsers do not have this # to make the SVG files visible in IE 9+ (other browsers do not have this
# requirement). # requirement).
@ -2695,11 +2758,12 @@ DOT_PATH =
DOTFILE_DIRS = DOTFILE_DIRS =
# The MSCFILE_DIRS tag can be used to specify one or more directories that # You can include diagrams made with dia in doxygen documentation. Doxygen will
# contain msc files that are included in the documentation (see the \mscfile # then run dia to produce the diagram and insert it in the documentation. The
# command). # DIA_PATH tag allows you to specify the directory where the dia binary resides.
# If left empty dia is assumed to be found in the default search path.
MSCFILE_DIRS = DIA_PATH =
# The DIAFILE_DIRS tag can be used to specify one or more directories that # The DIAFILE_DIRS tag can be used to specify one or more directories that
# contain dia files that are included in the documentation (see the \diafile # contain dia files that are included in the documentation (see the \diafile
@ -2776,3 +2840,19 @@ GENERATE_LEGEND = YES
# The default value is: YES. # The default value is: YES.
DOT_CLEANUP = YES DOT_CLEANUP = YES
# You can define message sequence charts within doxygen comments using the \msc
# command. If the MSCGEN_TOOL tag is left empty (the default), then doxygen will
# use a built-in version of mscgen tool to produce the charts. Alternatively,
# the MSCGEN_TOOL tag can also specify the name an external tool. For instance,
# specifying prog as the value, doxygen will call the tool as prog -T
# <outfile_format> -o <outputfile> <inputfile>. The external tool should support
# output file formats "png", "eps", "svg", and "ismap".
MSCGEN_TOOL =
# The MSCFILE_DIRS tag can be used to specify one or more directories that
# contain msc files that are included in the documentation (see the \mscfile
# command).
MSCFILE_DIRS =

View File

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

View File

@ -4,7 +4,7 @@ This guide describes the required changes to existing projects migrating from th
**The changes listed here are only the required changes that must be made**, and a short overview of options for migrating existing applications. **The changes listed here are only the required changes that must be made**, and a short overview of options for migrating existing applications.
For more information on the improvements and additions please refer to the [class documentation](https://h2zero.github.io/NimBLE-Arduino/annotated.html) and [Improvements and updates](Improvements_and_updates.md) For more information on the improvements and additions please refer to the [class documentation](https://h2zero.github.io/esp-nimble-cpp/annotated.html)
* [General Changes](#general-information) * [General Changes](#general-information)
* [Server](#server-api) * [Server](#server-api)
@ -20,12 +20,11 @@ For more information on the improvements and additions please refer to the [clas
* [Remote characteristics](#remote-characteristics) * [Remote characteristics](#remote-characteristics)
* [Client Callbacks](#client-callbacks) * [Client Callbacks](#client-callbacks)
* [Security](#client-security) * [Security](#client-security)
* [Scanning](#scan-api) * [BLE scan](#ble-scan)
* [General Security](#security-api) * [General Security](#security-api)
* [Configuration](#arduino-configuration) * [Configuration](#arduino-configuration)
<br/> <br/>
<a name="general-information"></a>
## General Information ## General Information
### Header Files ### Header Files
@ -48,10 +47,9 @@ For example `BLEAddress addr(11:22:33:44:55:66, 1)` will create the address obje
As this parameter is optional no changes to existing code are needed, it is mentioned here for information. As this parameter is optional no changes to existing code are needed, it is mentioned here for information.
`BLEAddress::getNative` (`NimBLEAddress::getNative`) returns a uint8_t pointer to the native address byte array. In this library the address bytes are stored in reverse order from the original library. This is due to the way the NimBLE stack expects addresses to be presented to it. All other functions such as `toString` are not affected as the endian change is made within them. `BLEAddress::getNative` is now named `NimBLEAddress::getBase` and returns a pointer to `const ble_addr_t` instead of a pointer to the address value.
<br/> <br/>
<a name="server-api"></a>
## Server API ## Server API
Creating a `BLEServer` instance is the same as original, no changes required. Creating a `BLEServer` instance is the same as original, no changes required.
For example `BLEDevice::createServer()` will work just as it did before. For example `BLEDevice::createServer()` will work just as it did before.
@ -72,7 +70,7 @@ void onDisconnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo, int reason)`
``` ```
<br/> <br/>
`BLEServerCallbacks::onMtuChanged` (`NimBLEServerCallbacks::onMtuChanged`) takes the parameter `NimBLEConnInfo & connInfo` instead of `esp_ble_gatts_cb_param_t`, which has methods to get information about the connected peer. `BLEServerCallbacks::onMtuChanged` is now (`NimBLEServerCallbacks::onMtuChange`) and takes the parameter `NimBLEConnInfo & connInfo` instead of `esp_ble_gatts_cb_param_t`, which has methods to get information about the connected peer.
``` ```
onMTUChange(uint16_t MTU, NimBLEConnInfo& connInfo) onMTUChange(uint16_t MTU, NimBLEConnInfo& connInfo)
@ -81,13 +79,11 @@ onMTUChange(uint16_t MTU, NimBLEConnInfo& connInfo)
**Note:** All callback methods have default implementations which allows the application to implement only the methods applicable. **Note:** All callback methods have default implementations which allows the application to implement only the methods applicable.
<br/> <br/>
<a name="services"></a>
### Services ### Services
Creating a `BLEService` (`NimBLEService`) instance is the same as original, no changes required. Creating a `BLEService` (`NimBLEService`) instance is the same as original, no changes required.
For example `BLEServer::createService(SERVICE_UUID)` will work just as it did before. For example `BLEServer::createService(SERVICE_UUID)` will work just as it did before.
<br/> <br/>
<a name="characteristics"></a>
### Characteristics ### Characteristics
`BLEService::createCharacteristic` (`NimBLEService::createCharacteristic`) is used the same way as originally except the properties parameter has changed. `BLEService::createCharacteristic` (`NimBLEService::createCharacteristic`) is used the same way as originally except the properties parameter has changed.
@ -137,8 +133,8 @@ BLECharacteristic *pCharacteristic = pService->createCharacteristic(
``` ```
<br/> <br/>
<a name="characteristic-callbacks"></a>
#### Characteristic callbacks #### Characteristic callbacks
`BLECharacteristicCallbacks` (`NimBLECharacteristicCallbacks`) has a new method `NimBLECharacteristicCallbacks::onSubscribe` which is called when a client subscribes to notifications/indications. `BLECharacteristicCallbacks` (`NimBLECharacteristicCallbacks`) has a new method `NimBLECharacteristicCallbacks::onSubscribe` which is called when a client subscribes to notifications/indications.
`BLECharacteristicCallbacks::onRead` (`NimBLECharacteristicCallbacks::onRead`) only has a single callback declaration, which takes an additional (required) parameter of `NimBLEConnInfo& connInfo`, which provides connection information about the peer. `BLECharacteristicCallbacks::onRead` (`NimBLECharacteristicCallbacks::onRead`) only has a single callback declaration, which takes an additional (required) parameter of `NimBLEConnInfo& connInfo`, which provides connection information about the peer.
@ -168,7 +164,6 @@ my_struct_t myStruct = pChr->getValue<my_struct_t>();
``` ```
<br/> <br/>
<a name="descriptors"></a>
### Descriptors ### Descriptors
Descriptors are now created using the `NimBLECharacteristic::createDescriptor` method. Descriptors are now created using the `NimBLECharacteristic::createDescriptor` method.
@ -179,8 +174,7 @@ NimBLE automatically creates the 0x2902 descriptor if a characteristic has a not
It was no longer useful to have a class for the 0x2902 descriptor as a new callback `NimBLECharacteristicCallbacks::onSubscribe` was added It was no longer useful to have a class for the 0x2902 descriptor as a new callback `NimBLECharacteristicCallbacks::onSubscribe` was added
to handle callback functionality and the client subscription status is handled internally. to handle callback functionality and the client subscription status is handled internally.
**Note:** Attempting to create a 0x2902 descriptor will trigger an assert to notify the error, **Note:** Attempting to create a 0x2902 descriptor will trigger a warning message and flag it internally as removed and will not be functional.
allowing the creation of it would cause a fault in the NimBLE stack.
All other descriptors are now created just as characteristics are by using the `NimBLECharacteristic::createDescriptor` method (except 0x2904, see below). All other descriptors are now created just as characteristics are by using the `NimBLECharacteristic::createDescriptor` method (except 0x2904, see below).
Which are defined as: Which are defined as:
@ -197,7 +191,7 @@ NimBLEDescriptor* createDescriptor(NimBLEUUID uuid,
NIMBLE_PROPERTY::WRITE, NIMBLE_PROPERTY::WRITE,
uint16_t max_len = 100); uint16_t max_len = 100);
``` ```
##### Example #### Example
``` ```
pDescriptor = pCharacteristic->createDescriptor("ABCD", pDescriptor = pCharacteristic->createDescriptor("ABCD",
NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::READ |
@ -208,27 +202,18 @@ pDescriptor = pCharacteristic->createDescriptor("ABCD",
Would create a descriptor with the UUID 0xABCD, publicly readable but only writable if paired/bonded (encrypted) and has a max value length of 25 bytes. Would create a descriptor with the UUID 0xABCD, publicly readable but only writable if paired/bonded (encrypted) and has a max value length of 25 bytes.
<br/> <br/>
For the 0x2904, there is a special class that is created when you call `createDescriptor("2904"). For the 0x2904, there is a specialized class that is created through `NimBLECharacteristic::create2904` which returns a pointer to a `NimBLE2904` instance which has specific
functions for handling the data expect in the Characteristic Presentation Format Descriptor specification.
The pointer returned is of the base class `NimBLEDescriptor` but the call will create the derived class of `NimBLE2904` so you must cast the returned pointer to
`NimBLE2904` to access the specific class methods.
##### Example
```
p2904 = (NimBLE2904*)pCharacteristic->createDescriptor("2904");
```
<br/> <br/>
<a name="descriptor-callbacks"></a>
#### Descriptor callbacks #### Descriptor callbacks
> `BLEDescriptorCallbacks::onRead` (`NimBLEDescriptorCallbacks::onRead`) > `BLEDescriptorCallbacks::onRead` (`NimBLEDescriptorCallbacks::onRead`)
`BLEDescriptorCallbacks::onwrite` (`NimBLEDescriptorCallbacks::onwrite`) `BLEDescriptorCallbacks::onWrite` (`NimBLEDescriptorCallbacks::onWrite`)
The above descriptor callbacks take an additional (required) parameter `NimBLEConnInfo& connInfo`, which contains the connection information of the peer. The above descriptor callbacks take an additional (required) parameter `NimBLEConnInfo& connInfo`, which contains the connection information of the peer.
<br/> <br/>
<a name="server-security"></a>
### Server Security ### Server Security
Security is set on the characteristic or descriptor properties by applying one of the following: Security is set on the characteristic or descriptor properties by applying one of the following:
> NIMBLE_PROPERTY::READ_ENC > NIMBLE_PROPERTY::READ_ENC
@ -245,7 +230,6 @@ When a peer wants to read or write a characteristic or descriptor with any of th
This can be changed to use passkey authentication or numeric comparison. See [Security API](#security-api) for details. This can be changed to use passkey authentication or numeric comparison. See [Security API](#security-api) for details.
<br/> <br/>
<a name="advertising-api"></a>
## Advertising API ## Advertising API
Advertising works the same as the original API except: Advertising works the same as the original API except:
@ -255,11 +239,9 @@ Calling `NimBLEAdvertising::setAdvertisementData` will entirely replace any data
> BLEAdvertising::start (NimBLEAdvertising::start) > BLEAdvertising::start (NimBLEAdvertising::start)
Now takes 2 optional parameters, the first is the duration to advertise for (in milliseconds), the second is a callback that is invoked when advertising ends and takes a pointer to a `NimBLEAdvertising` object (similar to the `NimBLEScan::start` API). Now takes 2 optional parameters, the first is the duration to advertise for (in milliseconds), the second `NimBLEAddress` to direct advertising to a specific device.
This provides an opportunity to update the advertisement data if desired.
<br/> <br/>
<a name="client-api"></a>
## Client API ## Client API
Client instances are created just as before with `BLEDevice::createClient` (`NimBLEDevice::createClient`). Client instances are created just as before with `BLEDevice::createClient` (`NimBLEDevice::createClient`).
@ -268,15 +250,17 @@ Multiple client instances can be created, up to the maximum number of connection
`BLEClient::connect`(`NimBLEClient::connect`) Has had it's parameters altered. `BLEClient::connect`(`NimBLEClient::connect`) Has had it's parameters altered.
Defined as: Defined as:
> NimBLEClient::connect(bool deleteServices = true); > NimBLEClient::connect(bool deleteServices = true, , bool asyncConnect = false, bool exchangeMTU = true);
> NimBLEClient::connect(NimBLEAdvertisedDevice\* device, bool deleteServices = true); > NimBLEClient::connect(const NimBLEAddress& address, bool deleteAttributes = true, bool asyncConnect = false, bool exchangeMTU = true);
> NimBLEClient::connect(NimBLEAddress address, bool deleteServices = true); > NimBLEClient::connect(const NimBLEAdvertisedDevice* device, bool deleteServices = true, bool asyncConnect = false, bool exchangeMTU = true);
The type parameter has been removed and a new bool parameter has been added to indicate if the client should delete the attribute database previously retrieved (if applicable) for the peripheral, default value is true. The type parameter has been removed and a new bool parameter has been added to indicate if the client should delete the attribute database previously retrieved (if applicable) for the peripheral, default value is true.
If set to false the client will use the attribute database it retrieved from the peripheral when previously connected. If set to false the client will use the attribute database it retrieved from the peripheral when previously connected. This allows for faster connections and power saving if the devices dropped connection and are reconnecting.
This allows for faster connections and power saving if the devices dropped connection and are reconnecting. The parameter `bool asyncConnect` if true, will cause the client to send the connect command to the stack and return immediately without blocking. The return value will represent wether the command was sent successfully or not and the `NimBLEClientCallbacks::onConnect` or `NimBLEClientCallbacks::onConnectFail` will be called when the operation is complete.
The parameter `bool exchangeMTU` if true, will cause the client to perform the exchange MTU process upon connecting. If false the MTU exchange will need to be performed by the application by calling `NimBLEClient::exchangeMTU`. If the connection is only sending small payloads it may be advantageous to not exchange the MTU to gain performance in the connection process.
<br/> <br/>
> `BLEClient::getServices` (`NimBLEClient::getServices`) > `BLEClient::getServices` (`NimBLEClient::getServices`)
@ -290,7 +274,6 @@ Also now returns a pointer to `std::vector` instead of `std::map`.
**Added:** `NimBLEClient::discoverAttributes` for the user to discover all the peripheral attributes to replace the the removed automatic functionality. **Added:** `NimBLEClient::discoverAttributes` for the user to discover all the peripheral attributes to replace the the removed automatic functionality.
<br/> <br/>
<a name="remote-services"></a>
### Remote Services ### Remote Services
`BLERemoteService` (`NimBLERemoteService`) Methods remain mostly unchanged with the exceptions of: `BLERemoteService` (`NimBLERemoteService`) Methods remain mostly unchanged with the exceptions of:
@ -301,12 +284,10 @@ This method has been removed.
> `BLERemoteService::getCharacteristics` (`NimBLERemoteService::getCharacteristics`) > `BLERemoteService::getCharacteristics` (`NimBLERemoteService::getCharacteristics`)
This method now takes an optional (bool) parameter to indicate if the characteristics should be retrieved from the server (true) or This method now takes an optional (bool) parameter to indicate if the characteristics should be retrieved from the server (true) or the currently known database returned, default = false.
the currently known database returned (false : default).
Also now returns a pointer to `std::vector` instead of `std::map`. Also now returns a pointer to `std::vector` instead of `std::map`.
<br/> <br/>
<a name="remote-characteristics"></a>
### Remote Characteristics ### Remote Characteristics
`BLERemoteCharacteristic` (`NimBLERemoteCharacteristic`) `BLERemoteCharacteristic` (`NimBLERemoteCharacteristic`)
There have been a few changes to the methods in this class: There have been a few changes to the methods in this class:
@ -326,12 +307,12 @@ Has been removed.
Are the new methods added to replace it. Are the new methods added to replace it.
<br/> <br/>
> `BLERemoteCharacteristic::readUInt8` (`NimBLERemoteCharacteristic::readUInt8`) > `BLERemoteCharacteristic::readUInt8` (`NimBLERemoteCharacteristic::readUInt8`)
> `BLERemoteCharacteristic::readUInt16` (`NimBLERemoteCharacteristic::readUInt16`) > `BLERemoteCharacteristic::readUInt16` (`NimBLERemoteCharacteristic::readUInt16`)
> `BLERemoteCharacteristic::readUInt32` (`NimBLERemoteCharacteristic::readUInt32`) > `BLERemoteCharacteristic::readUInt32` (`NimBLERemoteCharacteristic::readUInt32`)
> `BLERemoteCharacteristic::readFloat` (`NimBLERemoteCharacteristic::readFloat`) > `BLERemoteCharacteristic::readFloat` (`NimBLERemoteCharacteristic::readFloat`)
Are **deprecated** a template: `NimBLERemoteCharacteristic::readValue<type\>(time_t\*, bool)` has been added to replace them. Are **removed** a template: `NimBLERemoteCharacteristic::readValue<type\>(time_t\*, bool)` has been added to replace them.
<br/> <br/>
> `BLERemoteCharacteristic::readRawData` > `BLERemoteCharacteristic::readRawData`
@ -339,10 +320,10 @@ Are **deprecated** a template: `NimBLERemoteCharacteristic::readValue<type\>(tim
**Has been removed from the API** **Has been removed from the API**
Originally it stored an unnecessary copy of the data and was returning a `uint8_t` pointer to volatile internal data. Originally it stored an unnecessary copy of the data and was returning a `uint8_t` pointer to volatile internal data.
The user application should use `NimBLERemoteCharacteristic::readValue` or `NimBLERemoteCharacteristic::getValue`. The user application should use `NimBLERemoteCharacteristic::readValue` or `NimBLERemoteCharacteristic::getValue`.
To obtain a copy of the data, then cast the returned std::string to the type required such as: To obtain a copy of the data as a `NimBLEAttValue` instance and use the `NimBLEAttValue::data` member function to obtain the pointer.
``` ```
std::string value = pChr->readValue(); NimBLEAttValue value = pChr->readValue();
uint8_t *data = (uint8_t*)value.data(); const uint8_t *data = value.data();
``` ```
Alternatively use the `readValue` template: Alternatively use the `readValue` template:
``` ```
@ -352,12 +333,10 @@ my_struct_t myStruct = pChr->readValue<my_struct_t>();
> `BLERemoteCharacteristic::getDescriptors` (`NimBLERemoteCharacteristic::getDescriptors`) > `BLERemoteCharacteristic::getDescriptors` (`NimBLERemoteCharacteristic::getDescriptors`)
This method now takes an optional (bool) parameter to indicate if the descriptors should be retrieved from the server (true) or This method now takes an optional (bool) parameter to indicate if the descriptors should be retrieved from the server (true) or the currently known database returned, default = false.
the currently known database returned (false : default).
Also now returns a pointer to `std::vector` instead of `std::map`. Also now returns a pointer to `std::vector` instead of `std::map`.
<br/> <br/>
<a name="client-callbacks"></a>
### Client callbacks ### Client callbacks
> `BLEClientCallbacks::onDisconnect` (`NimBLEClientCallbacks::onDisconnect`) > `BLEClientCallbacks::onDisconnect` (`NimBLEClientCallbacks::onDisconnect`)
@ -365,31 +344,33 @@ Also now returns a pointer to `std::vector` instead of `std::map`.
This now takes a second parameter `int reason` which provides the reason code for disconnection. This now takes a second parameter `int reason` which provides the reason code for disconnection.
<br/> <br/>
<a name="client-security"></a>
### Client Security ### Client Security
The client will automatically initiate security when the peripheral responds that it's required. The client will automatically initiate security when the peripheral responds that it's required.
The default configuration will use "just-works" pairing with no bonding, if you wish to enable bonding see below. The default configuration will use "just-works" pairing with no bonding, if you wish to enable bonding see below.
<br/> <br/>
<a name="scan-api"></a>
## BLE Scan ## BLE Scan
The scan API is mostly unchanged from the original except for `NimBLEScan::start`, in which the duration parameter is now in milliseconds instead of seconds. The scan API is mostly unchanged from the original except for `NimBLEScan::start`, which has the following changes:
* The duration parameter is now in milliseconds instead of seconds.
* The callback parameter has been removed.
* A new parameter `bool restart` has been added, when set to true to restart the scan if already in progress and clear the duplicate cache.
The blocking overload of `NimBLEScan::start` has been replaced by an overload of `NimBLEScan::getResults` with the same parameters.
<br/> <br/>
<a name="security-api"></a>
## Security API ## Security API
Security operations have been moved to `BLEDevice` (`NimBLEDevice`). Security operations have been moved to `BLEDevice` (`NimBLEDevice`).
The security callback methods are now incorporated in the `NimBLEServerCallbacks` / `NimBLEClientCallbacks` classes. The security callback methods are now incorporated in the `NimBLEServerCallbacks` / `NimBLEClientCallbacks` classes.
The callback methods are: The callback methods are:
> `bool onConfirmPIN(const NimBLEConnInfo& connInfo, uint32_t pin)` > `bool onConfirmPasskey(NimBLEConnInfo& connInfo, uint32_t pin)`
Receives the pin when using numeric comparison authentication. Receives the pin when using numeric comparison authentication.
Call `NimBLEDevice::injectConfirmPIN(connInfo, true);` to accept or `NimBLEDevice::injectConfirmPIN(connInfo, false);` to reject. Call `NimBLEDevice::injectConfirmPasskey(connInfo, true);` to accept or `NimBLEDevice::injectConfirmPasskey(connInfo, false);` to reject.
<br/> <br/>
> `void onPassKeyEntry(const NimBLEConnInfo& connInfo)` > `void onPassKeyEntry(NimBLEConnInfo& connInfo)`
Client callback; client should respond with the passkey (pin) by calling `NimBLEDevice::injectPassKey(connInfo, 123456);` Client callback; client should respond with the passkey (pin) by calling `NimBLEDevice::injectPassKey(connInfo, 123456);`
<br/> <br/>
@ -399,7 +380,7 @@ Client callback; client should respond with the passkey (pin) by calling `NimBLE
Server callback; should return the passkey (pin) expected from the client. Server callback; should return the passkey (pin) expected from the client.
<br/> <br/>
> `void onAuthenticationComplete(const NimBLEConnInfo& connInfo)` > `void onAuthenticationComplete(NimBLEConnInfo& connInfo)`
Authentication complete, success or failed information is available from the `NimBLEConnInfo` methods. Authentication complete, success or failed information is available from the `NimBLEConnInfo` methods.
<br/> <br/>
@ -426,7 +407,6 @@ If we are the initiator of the security procedure this sets the keys we will dis
Sets the keys we are willing to accept from the peer during pairing. Sets the keys we are willing to accept from the peer during pairing.
<br/> <br/>
<a name="arduino-configuration"></a>
## Arduino Configuration ## Arduino Configuration
Unlike the original library pre-packaged in the esp32-arduino, this library has all the configuration options that are normally set in menuconfig available in the *src/nimconfig.h* file. Unlike the original library pre-packaged in the esp32-arduino, this library has all the configuration options that are normally set in menuconfig available in the *src/nimconfig.h* file.

View File

@ -21,7 +21,6 @@ If you're not creating a server or do not want to advertise a name, simply pass
This can be called any time you wish to use BLE functions and does not need to be called from app_main(IDF) or setup(Arduino) but usually is. This can be called any time you wish to use BLE functions and does not need to be called from app_main(IDF) or setup(Arduino) but usually is.
<br/> <br/>
<a name="creating-a-server"></a>
## Creating a Server ## Creating a Server
BLE servers perform 2 tasks, they advertise their existence for clients to find them and they provide services which contain information for the connecting client. BLE servers perform 2 tasks, they advertise their existence for clients to find them and they provide services which contain information for the connecting client.
@ -38,9 +37,7 @@ For this example we will keep it simple and use a 16 bit value: ABCD.
``` ```
#include "NimBLEDevice.h" #include "NimBLEDevice.h"
// void setup() in Arduino extern "C" void app_main(void) {
void app_main(void)
{
NimBLEDevice::init("NimBLE"); NimBLEDevice::init("NimBLE");
NimBLEServer *pServer = NimBLEDevice::createServer(); NimBLEServer *pServer = NimBLEDevice::createServer();
@ -80,9 +77,7 @@ The function call will simply be `pService->createCharacteristic("1234");`
``` ```
#include "NimBLEDevice.h" #include "NimBLEDevice.h"
// void setup() in Arduino extern "C" void app_main(void) {
void app_main(void)
{
NimBLEDevice::init("NimBLE"); NimBLEDevice::init("NimBLE");
NimBLEServer *pServer = NimBLEDevice::createServer(); NimBLEServer *pServer = NimBLEDevice::createServer();
@ -100,12 +95,13 @@ There are many different types you can send as parameters for the value but for
`pCharacteristic->setValue("Hello BLE");` `pCharacteristic->setValue("Hello BLE");`
Next we need to advertise for connections. Next we need to advertise for connections.
To do this we create an instance of `NimBLEAdvertising` add our service to it (optional) and start advertisng. To do this we create an instance of `NimBLEAdvertising` add our service to it (optional) and start advertising.
**The code for this will be:** **The code for this will be:**
``` ```
NimBLEAdvertising *pAdvertising = NimBLEDevice::getAdvertising(); // create advertising instance NimBLEAdvertising *pAdvertising = NimBLEDevice::getAdvertising(); // create advertising instance
pAdvertising->addServiceUUID("ABCD"); // tell advertising the UUID of our service pAdvertising->addServiceUUID("ABCD"); // advertise the UUID of our service
pAdvertising->setName("NimBLE"); // advertise the device name
pAdvertising->start(); // start advertising pAdvertising->start(); // start advertising
``` ```
That's it, this will be enough to create a BLE server with a service and a characteristic and advertise for client connections. That's it, this will be enough to create a BLE server with a service and a characteristic and advertise for client connections.
@ -114,9 +110,7 @@ That's it, this will be enough to create a BLE server with a service and a chara
``` ```
#include "NimBLEDevice.h" #include "NimBLEDevice.h"
// void setup() in Arduino extern "C" void app_main(void) {
void app_main(void)
{
NimBLEDevice::init("NimBLE"); NimBLEDevice::init("NimBLE");
NimBLEServer *pServer = NimBLEDevice::createServer(); NimBLEServer *pServer = NimBLEDevice::createServer();
@ -127,7 +121,8 @@ void app_main(void)
pCharacteristic->setValue("Hello BLE"); pCharacteristic->setValue("Hello BLE");
NimBLEAdvertising *pAdvertising = NimBLEDevice::getAdvertising(); NimBLEAdvertising *pAdvertising = NimBLEDevice::getAdvertising();
pAdvertising->addServiceUUID("ABCD"); pAdvertising->addServiceUUID("ABCD"); // advertise the UUID of our service
pAdvertising->setName("NimBLE"); // advertise the device name
pAdvertising->start(); pAdvertising->start();
} }
``` ```
@ -137,7 +132,6 @@ Now if you scan with your phone using nRFConnect or any other BLE app you should
For more advanced features and options please see the server examples in the examples folder. For more advanced features and options please see the server examples in the examples folder.
<br/> <br/>
<a name="creating-a-client"></a>
## Creating a Client ## Creating a Client
BLE clients perform 2 tasks, they scan for advertising servers and form connections to them to read and write to their characteristics/descriptors. BLE clients perform 2 tasks, they scan for advertising servers and form connections to them to read and write to their characteristics/descriptors.
@ -146,7 +140,7 @@ After initializing the NimBLE stack we create a scan instance by calling `NimBLE
Once we have created the scan we can start looking for advertising servers. Once we have created the scan we can start looking for advertising servers.
To do this we call `NimBLEScan::start(duration)`, the duration parameter is a uint32_t that specifies the number of milliseconds to scan for, To do this we call `NimBLEScan::getResults(duration)`, the duration parameter is a uint32_t that specifies the number of milliseconds to scan for,
passing 0 will scan forever. passing 0 will scan forever.
In this example we will scan for 10 seconds. This is a blocking function (a non blocking overload is also available). In this example we will scan for 10 seconds. This is a blocking function (a non blocking overload is also available).
@ -156,9 +150,7 @@ This call returns an instance of `NimBLEScanResults` when the scan completes whi
``` ```
#include "NimBLEDevice.h" #include "NimBLEDevice.h"
// void setup() in Arduino extern "C" void app_main(void) {
void app_main(void)
{
NimBLEDevice::init(""); NimBLEDevice::init("");
NimBLEScan *pScan = NimBLEDevice::getScan(); NimBLEScan *pScan = NimBLEDevice::getScan();
@ -170,7 +162,7 @@ void app_main(void)
Now that we have scanned we need to check the results for any advertisers we are interested in connecting to. Now that we have scanned we need to check the results for any advertisers we are interested in connecting to.
To do this we iterate through the results and check if any of the devices found are advertising the service we want `ABCD`. To do this we iterate through the results and check if any of the devices found are advertising the service we want `ABCD`.
Each result in `NimBLEScanResults` is a `NimBLEAdvertisedDevice` instance that we can access data from. Each result in `NimBLEScanResults` is a `const NimBLEAdvertisedDevice*` that we can access data from.
We will check each device found for the `ABCD` service by calling `NimBLEAdvertisedDevice::isAdvertisingService`. We will check each device found for the `ABCD` service by calling `NimBLEAdvertisedDevice::isAdvertisingService`.
This takes an instance of `NimBLEUUID` as a parameter so we will need to create one. This takes an instance of `NimBLEUUID` as a parameter so we will need to create one.
@ -179,11 +171,11 @@ This takes an instance of `NimBLEUUID` as a parameter so we will need to create
``` ```
NimBLEUUID serviceUuid("ABCD"); NimBLEUUID serviceUuid("ABCD");
for(int i = 0; i < results.getCount(); i++) { for (int i = 0; i < results.getCount(); i++) {
NimBLEAdvertisedDevice device = results.getDevice(i); const NimBLEAdvertisedDevice *device = results.getDevice(i);
if (device.isAdvertisingService(serviceUuid)) { if (device->isAdvertisingService(serviceUuid)) {
// create a client and connect // create a client and connect
} }
} }
``` ```
@ -200,16 +192,16 @@ This takes a pointer to the `NimBLEAdvertisedDevice` and returns `true` if succe
``` ```
NimBLEUUID serviceUuid("ABCD"); NimBLEUUID serviceUuid("ABCD");
for(int i = 0; i < results.getCount(); i++) { for (int i = 0; i < results.getCount(); i++) {
NimBLEAdvertisedDevice device = results.getDevice(i); const NimBLEAdvertisedDevice *device = results.getDevice(i);
if (device.isAdvertisingService(serviceUuid)) { if (device->isAdvertisingService(serviceUuid)) {
NimBLEClient *pClient = NimBLEDevice::createClient(); NimBLEClient *pClient = NimBLEDevice::createClient();
if(pClient->connect(&device)) { if (pClient->connect(&device)) {
//success //success
} else { } else {
// failed to connect // failed to connect
} }
} }
} }
@ -231,11 +223,15 @@ Finally we will read the characteristic value with `NimBLERemoteCharacteristic::
``` ```
NimBLEUUID serviceUuid("ABCD"); NimBLEUUID serviceUuid("ABCD");
for(int i = 0; i < results.getCount(); i++) { for (int i = 0; i < results.getCount(); i++) {
NimBLEAdvertisedDevice device = results.getDevice(i); const NimBLEAdvertisedDevice *device = results.getDevice(i);
if (device.isAdvertisingService(serviceUuid)) { if (device->isAdvertisingService(serviceUuid)) {
NimBLEClient *pClient = NimBLEDevice::createClient(); NimBLEClient *pClient = NimBLEDevice::createClient();
if (!pClient) { // Make sure the client was created
break;
}
if (pClient->connect(&device)) { if (pClient->connect(&device)) {
NimBLERemoteService *pService = pClient->getService(serviceUuid); NimBLERemoteService *pService = pClient->getService(serviceUuid);
@ -249,7 +245,7 @@ for(int i = 0; i < results.getCount(); i++) {
} }
} }
} else { } else {
// failed to connect // failed to connect
} }
} }
} }
@ -264,12 +260,16 @@ This is done by calling `NimBLEDevice::deleteClient`.
``` ```
NimBLEUUID serviceUuid("ABCD"); NimBLEUUID serviceUuid("ABCD");
for(int i = 0; i < results.getCount(); i++) { for (int i = 0; i < results.getCount(); i++) {
NimBLEAdvertisedDevice device = results.getDevice(i); const NimBLEAdvertisedDevice *device = results.getDevice(i);
if (device.isAdvertisingService(serviceUuid)) { if (device->isAdvertisingService(serviceUuid)) {
NimBLEClient *pClient = NimBLEDevice::createClient(); NimBLEClient *pClient = NimBLEDevice::createClient();
if (!pClient) { // Make sure the client was created
break;
}
if (pClient->connect(&device)) { if (pClient->connect(&device)) {
NimBLERemoteService *pService = pClient->getService(serviceUuid); NimBLERemoteService *pService = pClient->getService(serviceUuid);
@ -282,7 +282,7 @@ for(int i = 0; i < results.getCount(); i++) {
} }
} }
} else { } else {
// failed to connect // failed to connect
} }
NimBLEDevice::deleteClient(pClient); NimBLEDevice::deleteClient(pClient);
@ -296,37 +296,39 @@ Note that there is no need to disconnect as that will be done when deleting the
``` ```
#include "NimBLEDevice.h" #include "NimBLEDevice.h"
// void setup() in Arduino extern "C" void app_main(void) {
void app_main(void)
{
NimBLEDevice::init(""); NimBLEDevice::init("");
NimBLEScan *pScan = NimBLEDevice::getScan(); NimBLEScan *pScan = NimBLEDevice::getScan();
NimBLEScanResults results = pScan->start(10 * 1000); NimBLEScanResults results = pScan->getResults(10 * 1000);
NimBLEUUID serviceUuid("ABCD"); NimBLEUUID serviceUuid("ABCD");
for(int i = 0; i < results.getCount(); i++) { for (int i = 0; i < results.getCount(); i++) {
NimBLEAdvertisedDevice device = results.getDevice(i); const NimBLEAdvertisedDevice *device = results.getDevice(i);
if (device.isAdvertisingService(serviceUuid)) { if (device->isAdvertisingService(serviceUuid)) {
NimBLEClient *pClient = NimBLEDevice::createClient(); NimBLEClient *pClient = NimBLEDevice::createClient();
if (!pClient) { // Make sure the client was created
break;
}
if (pClient->connect(&device)) { if (pClient->connect(&device)) {
NimBLERemoteService *pService = pClient->getService(serviceUuid); NimBLERemoteService *pService = pClient->getService(serviceUuid);
if (pService != nullptr) { if (pService != nullptr) {
NimBLERemoteCharacteristic *pCharacteristic = pService->getCharacteristic("1234"); NimBLERemoteCharacteristic *pCharacteristic = pService->getCharacteristic("1234");
if (pCharacteristic != nullptr) { if (pCharacteristic != nullptr) {
std::string value = pCharacteristic->readValue(); std::string value = pCharacteristic->readValue();
// print or do whatever you need with the value // print or do whatever you need with the value
} }
} }
} else { } else {
// failed to connect // failed to connect
} }
NimBLEDevice::deleteClient(pClient); NimBLEDevice::deleteClient(pClient);
} }
} }
@ -336,4 +338,3 @@ void app_main(void)
For more advanced features and options please see the client examples in the examples folder. For more advanced features and options please see the client examples in the examples folder.
<br/> <br/>

View File

@ -1,5 +1,4 @@
# Overview # Overview
This is a C++ BLE library for the ESP32 that uses the NimBLE host stack instead of bluedroid. 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++ & Arduino BLE API by while adding new features 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. and making improvements in performance, resource use, and stability.
@ -14,7 +13,7 @@ It is more suited to resource constrained devices than bluedroid and has now bee
<br/> <br/>
# ESP-IDF Installation # ESP-IDF Installation
### v4.0+ ## v4.0+
Download as .zip and extract or clone into the components folder in your esp-idf project. Download as .zip and extract or clone into the components folder in your esp-idf project.
Run menuconfig, go to `Component config->Bluetooth` enable Bluetooth and in `Bluetooth host` NimBLE. Run menuconfig, go to `Component config->Bluetooth` enable Bluetooth and in `Bluetooth host` NimBLE.
@ -23,34 +22,23 @@ Configure settings in `NimBLE Options`.
Call `NimBLEDevice::init` in `app_main`. Call `NimBLEDevice::init` in `app_main`.
<br/> <br/>
### v3.2 & v3.3
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`.
<br/>
# Using # Using
This library is intended to be compatible with the original ESP32 BLE functions and types with minor changes. This library is intended to be compatible with the original ESP32 BLE functions and types with minor changes.
If you have not used the original Bluedroid library please refer to the [New user guide](New_user_guide.md). If you have not used the original Bluedroid library please refer to the [New user guide](New_user_guide.md).
If you are familiar with the original library, see: [The migration guide](Migration_guide.md) for details. 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.
For more advanced usage see [Usage tips](Usage_tips.md) for more performance and optimization. For more advanced usage see [Usage tips](Usage_tips.md) for more performance and optimization.
<br/> <br/>
# Need help? Have a question or suggestion? # Sponsors
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) Thank you to all the sponsors who support this project!
If you use this library for a commercial product please consider [sponsoring the development](https://github.com/sponsors/h2zero) to ensure the continued updates and maintenance.
<br/> <br/>
# Acknowledgments # 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. * [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. * [beegee-tokyo](https://github.com/beegee-tokyo) for contributing your time to test/debug and contributing the beacon examples.
* [Jeroen88](https://github.com/Jeroen88) for the amazing help debugging and improving the client code. * [Jeroen88](https://github.com/Jeroen88) for the amazing help debugging and improving the client code.

View File

@ -1,3 +0,0 @@
PROJECT_NAME := NimBLE_Client
include $(IDF_PATH)/make/project.mk

View File

@ -1,372 +0,0 @@
/** NimBLE_Client Demo:
*
* Demonstrates many of the available features of the NimBLE client library.
*
* Created: on March 24 2020
* Author: H2zero
*
*/
#include <NimBLEDevice.h>
extern "C" {void app_main(void);}
static NimBLEAdvertisedDevice* advDevice;
static bool doConnect = false;
static uint32_t scanTime = 0; /** scan time in milliseconds, 0 = scan forever */
/** None of these are required as they will be handled by the library with defaults. **
** Remove as you see fit for your needs */
class ClientCallbacks : public NimBLEClientCallbacks {
void onConnect(NimBLEClient* pClient) {
printf("Connected\n");
/** After connection we should change the parameters if we don't need fast response times.
* These settings are 150ms interval, 0 latency, 450ms timout.
* Timeout should be a multiple of the interval, minimum is 100ms.
* I find a multiple of 3-5 * the interval works best for quick response/reconnect.
* Min interval: 120 * 1.25ms = 150, Max interval: 120 * 1.25ms = 150, 0 latency, 45 * 10ms = 450ms timeout
*/
pClient->updateConnParams(120,120,0,45);
}
void onDisconnect(NimBLEClient* pClient, int reason) {
printf("%s Disconnected, reason = %d - Starting scan\n",
pClient->getPeerAddress().toString().c_str(), reason);
NimBLEDevice::getScan()->start(scanTime);
}
/********************* Security handled here **********************
****** Note: these are the same return values as defaults ********/
void onPassKeyEntry(const NimBLEConnInfo& connInfo){
printf("Server Passkey Entry\n");
/** This should prompt the user to enter the passkey displayed
* on the peer device.
*/
NimBLEDevice::injectPassKey(connInfo, 123456);
};
void onConfirmPIN(const NimBLEConnInfo& connInfo, uint32_t pass_key){
printf("The passkey YES/NO number: %" PRIu32 "\n", pass_key);
/** Inject false if passkeys don't match. */
NimBLEDevice::injectConfirmPIN(connInfo, true);
};
/** Pairing process complete, we can check the results in connInfo */
void onAuthenticationComplete(const NimBLEConnInfo& connInfo){
if(!connInfo.isEncrypted()) {
printf("Encrypt connection failed - disconnecting\n");
/** Find the client with the connection handle provided in desc */
NimBLEDevice::getClientByID(connInfo.getConnHandle())->disconnect();
return;
}
}
};
/** Define a class to handle the callbacks when advertisments are received */
class scanCallbacks: public NimBLEScanCallbacks {
void onResult(NimBLEAdvertisedDevice* advertisedDevice) {
printf("Advertised Device found: %s\n", advertisedDevice->toString().c_str());
if(advertisedDevice->isAdvertisingService(NimBLEUUID("DEAD")))
{
printf("Found Our Service\n");
/** stop scan before connecting */
NimBLEDevice::getScan()->stop();
/** Save the device reference in a global for the client to use*/
advDevice = advertisedDevice;
/** Ready to connect now */
doConnect = true;
}
}
/** Callback to process the results of the completed scan or restart it */
void onScanEnd(NimBLEScanResults results) {
printf("Scan Ended\n");
}
};
/** Notification / Indication receiving handler callback */
void notifyCB(NimBLERemoteCharacteristic* pRemoteCharacteristic, uint8_t* pData, size_t length, bool isNotify){
std::string str = (isNotify == true) ? "Notification" : "Indication";
str += " from ";
str += pRemoteCharacteristic->getRemoteService()->getClient()->getPeerAddress().toString();
str += ": Service = " + pRemoteCharacteristic->getRemoteService()->getUUID().toString();
str += ", Characteristic = " + pRemoteCharacteristic->getUUID().toString();
str += ", Value = " + std::string((char*)pData, length);
printf("%s\n", str.c_str());
}
/** Create a single global instance of the callback class to be used by all clients */
static ClientCallbacks clientCB;
/** Handles the provisioning of clients and connects / interfaces with the server */
bool connectToServer() {
NimBLEClient* pClient = nullptr;
/** Check if we have a client we should reuse first **/
if(NimBLEDevice::getClientListSize()) {
/** Special case when we already know this device, we send false as the
* second argument in connect() to prevent refreshing the service database.
* This saves considerable time and power.
*/
pClient = NimBLEDevice::getClientByPeerAddress(advDevice->getAddress());
if(pClient){
if(!pClient->connect(advDevice, false)) {
printf("Reconnect failed\n");
return false;
}
printf("Reconnected client\n");
}
/** We don't already have a client that knows this device,
* we will check for a client that is disconnected that we can use.
*/
else {
pClient = NimBLEDevice::getDisconnectedClient();
}
}
/** No client to reuse? Create a new one. */
if(!pClient) {
if(NimBLEDevice::getClientListSize() >= NIMBLE_MAX_CONNECTIONS) {
printf("Max clients reached - no more connections available\n");
return false;
}
pClient = NimBLEDevice::createClient();
printf("New client created\n");
pClient->setClientCallbacks(&clientCB, false);
/** Set initial connection parameters: These settings are 15ms interval, 0 latency, 120ms timout.
* These settings are safe for 3 clients to connect reliably, can go faster if you have less
* connections. Timeout should be a multiple of the interval, minimum is 100ms.
* Min interval: 12 * 1.25ms = 15, Max interval: 12 * 1.25ms = 15, 0 latency, 12 * 10ms = 120ms timeout
*/
pClient->setConnectionParams(6,6,0,15);
/** Set how long we are willing to wait for the connection to complete (milliseconds), default is 30000. */
pClient->setConnectTimeout(5 * 1000);
if (!pClient->connect(advDevice)) {
/** Created a client but failed to connect, don't need to keep it as it has no data */
NimBLEDevice::deleteClient(pClient);
printf("Failed to connect, deleted client\n");
return false;
}
}
if(!pClient->isConnected()) {
if (!pClient->connect(advDevice)) {
printf("Failed to connect\n");
return false;
}
}
printf("Connected to: %s RSSI: %d\n",
pClient->getPeerAddress().toString().c_str(),
pClient->getRssi());
/** Now we can read/write/subscribe the charateristics of the services we are interested in */
NimBLERemoteService* pSvc = nullptr;
NimBLERemoteCharacteristic* pChr = nullptr;
NimBLERemoteDescriptor* pDsc = nullptr;
pSvc = pClient->getService("DEAD");
if(pSvc) { /** make sure it's not null */
pChr = pSvc->getCharacteristic("BEEF");
}
if(pChr) { /** make sure it's not null */
if(pChr->canRead()) {
printf("%s Value: %s\n",
pChr->getUUID().toString().c_str(),
pChr->readValue().c_str());
}
if(pChr->canWrite()) {
if(pChr->writeValue("Tasty")) {
printf("Wrote new value to: %s\n", pChr->getUUID().toString().c_str());
}
else {
/** Disconnect if write failed */
pClient->disconnect();
return false;
}
if(pChr->canRead()) {
printf("The value of: %s is now: %s\n",
pChr->getUUID().toString().c_str(),
pChr->readValue().c_str());
}
}
/** registerForNotify() has been removed and replaced with subscribe() / unsubscribe().
* Subscribe parameter defaults are: notifications=true, notifyCallback=nullptr, response=true.
* Unsubscribe parameter defaults are: response=true.
*/
if(pChr->canNotify()) {
//if(!pChr->registerForNotify(notifyCB)) {
if(!pChr->subscribe(true, notifyCB)) {
/** Disconnect if subscribe failed */
pClient->disconnect();
return false;
}
}
else if(pChr->canIndicate()) {
/** Send false as first argument to subscribe to indications instead of notifications */
//if(!pChr->registerForNotify(notifyCB, false)) {
if(!pChr->subscribe(false, notifyCB)) {
/** Disconnect if subscribe failed */
pClient->disconnect();
return false;
}
}
}
else{
printf("DEAD service not found.\n");
}
pSvc = pClient->getService("BAAD");
if(pSvc) { /** make sure it's not null */
pChr = pSvc->getCharacteristic("F00D");
}
if(pChr) { /** make sure it's not null */
if(pChr->canRead()) {
printf("%s Value: %s\n",
pChr->getUUID().toString().c_str(),
pChr->readValue().c_str());
}
pDsc = pChr->getDescriptor(NimBLEUUID("C01D"));
if(pDsc) { /** make sure it's not null */
printf("Descriptor: %s Value: %s\n",
pDsc->getUUID().toString().c_str(),
pDsc->readValue().c_str());
}
if(pChr->canWrite()) {
if(pChr->writeValue("No tip!")) {
printf("Wrote new value to: %s\n", pChr->getUUID().toString().c_str());
}
else {
/** Disconnect if write failed */
pClient->disconnect();
return false;
}
if(pChr->canRead()) {
printf("The value of: %s is now: %s\n",
pChr->getUUID().toString().c_str(),
pChr->readValue().c_str());
}
}
/** registerForNotify() has been deprecated and replaced with subscribe() / unsubscribe().
* Subscribe parameter defaults are: notifications=true, notifyCallback=nullptr, response=true.
* Unsubscribe parameter defaults are: response=true.
*/
if(pChr->canNotify()) {
//if(!pChr->registerForNotify(notifyCB)) {
if(!pChr->subscribe(true, notifyCB)) {
/** Disconnect if subscribe failed */
pClient->disconnect();
return false;
}
}
else if(pChr->canIndicate()) {
/** Send false as first argument to subscribe to indications instead of notifications */
//if(!pChr->registerForNotify(notifyCB, false)) {
if(!pChr->subscribe(false, notifyCB)) {
/** Disconnect if subscribe failed */
pClient->disconnect();
return false;
}
}
}
else{
printf("BAAD service not found.\n");
}
printf("Done with this device!\n");
return true;
}
void connectTask (void * parameter){
/** Loop here until we find a device we want to connect to */
for(;;) {
if(doConnect) {
doConnect = false;
/** Found a device we want to connect to, do it now */
if(connectToServer()) {
printf("Success! we should now be getting notifications, scanning for more!\n");
} else {
printf("Failed to connect, starting scan\n");
}
NimBLEDevice::getScan()->start(scanTime);
}
vTaskDelay(10/portTICK_PERIOD_MS);
}
vTaskDelete(NULL);
}
void app_main (void){
printf("Starting NimBLE Client\n");
/** Initialize NimBLE, no device name spcified as we are not advertising */
NimBLEDevice::init("");
/** Set the IO capabilities of the device, each option will trigger a different pairing method.
* BLE_HS_IO_KEYBOARD_ONLY - Passkey pairing
* BLE_HS_IO_DISPLAY_YESNO - Numeric comparison pairing
* BLE_HS_IO_NO_INPUT_OUTPUT - DEFAULT setting - just works pairing
*/
//NimBLEDevice::setSecurityIOCap(BLE_HS_IO_KEYBOARD_ONLY); // use passkey
//NimBLEDevice::setSecurityIOCap(BLE_HS_IO_DISPLAY_YESNO); //use numeric comparison
/** 2 different ways to set security - both calls achieve the same result.
* no bonding, no man in the middle protection, secure connections.
*
* These are the default values, only shown here for demonstration.
*/
//NimBLEDevice::setSecurityAuth(false, false, true);
NimBLEDevice::setSecurityAuth(/*BLE_SM_PAIR_AUTHREQ_BOND | BLE_SM_PAIR_AUTHREQ_MITM |*/ BLE_SM_PAIR_AUTHREQ_SC);
/** Optional: set the transmit power, default is -3db */
NimBLEDevice::setPower(ESP_PWR_LVL_P9); /** 12db */
/** Optional: set any devices you don't want to get advertisments from */
// NimBLEDevice::addIgnored(NimBLEAddress ("aa:bb:cc:dd:ee:ff"));
/** create new scan */
NimBLEScan* pScan = NimBLEDevice::getScan();
/** create a callback that gets called when advertisers are found */
pScan->setScanCallbacks (new scanCallbacks());
/** Set scan interval (how often) and window (how long) in milliseconds */
pScan->setInterval(400);
pScan->setWindow(100);
/** Active scan will gather scan response data from advertisers
* but will use more energy from both devices
*/
pScan->setActiveScan(true);
/** Start scanning for advertisers for the scan time specified (in milliseconds) 0 = forever
* Optional callback for when scanning stops.
*/
pScan->start(scanTime);
printf("Scanning for peripherals\n");
xTaskCreate(connectTask, "connectTask", 5000, NULL, 1, NULL);
}

View File

@ -1,12 +0,0 @@
# Override some defaults so BT stack is enabled
# in this example
#
# BT config
#
CONFIG_BT_ENABLED=y
CONFIG_BTDM_CTRL_MODE_BLE_ONLY=y
CONFIG_BTDM_CTRL_MODE_BR_EDR_ONLY=n
CONFIG_BTDM_CTRL_MODE_BTDM=n
CONFIG_BT_BLUEDROID_ENABLED=n
CONFIG_BT_NIMBLE_ENABLED=y

View File

@ -1,3 +0,0 @@
PROJECT_NAME := NimBLE_Server
include $(IDF_PATH)/make/project.mk

View File

@ -1,12 +0,0 @@
# Override some defaults so BT stack is enabled
# in this example
#
# BT config
#
CONFIG_BT_ENABLED=y
CONFIG_BTDM_CTRL_MODE_BLE_ONLY=y
CONFIG_BTDM_CTRL_MODE_BR_EDR_ONLY=n
CONFIG_BTDM_CTRL_MODE_BTDM=n
CONFIG_BT_BLUEDROID_ENABLED=n
CONFIG_BT_NIMBLE_ENABLED=y

View File

@ -3,5 +3,4 @@
cmake_minimum_required(VERSION 3.5) cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake) include($ENV{IDF_PATH}/tools/cmake/project.cmake)
set(SUPPORTED_TARGETS esp32c3 esp32s3)
project(NimBLE_extended_client) project(NimBLE_extended_client)

View File

@ -1,3 +0,0 @@
PROJECT_NAME := NimBLE_extended_client
include $(IDF_PATH)/make/project.mk

View File

@ -1,4 +0,0 @@
#
# "main" pseudo-component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)

View File

@ -1,71 +1,62 @@
/** NimBLE Extended Client Demo: /**
* NimBLE Extended Client Demo:
* *
* Demonstrates the Bluetooth 5.x client capabilities. * Demonstrates the Bluetooth 5.x client capabilities.
* *
* Created: on April 2 2022 * Created: on April 2 2022
* Author: H2zero * Author: H2zero
* */
*/
#include <NimBLEDevice.h>
extern "C" void app_main(void); #include <NimBLEDevice.h>
#define SERVICE_UUID "ABCD" #define SERVICE_UUID "ABCD"
#define CHARACTERISTIC_UUID "1234" #define CHARACTERISTIC_UUID "1234"
static NimBLEAdvertisedDevice* advDevice; static const NimBLEAdvertisedDevice* advDevice;
static bool doConnect = false; static bool doConnect = false;
static uint32_t scanTime = 10 * 1000; // In milliseconds, 0 = scan forever static uint32_t scanTimeMs = 10 * 1000; // In milliseconds, 0 = scan forever
/* Define the PHY's to use when connecting to peer devices, can be 1, 2, or all 3 (default).*/ /** Define the PHY's to use when connecting to peer devices, can be 1, 2, or all 3 (default).*/
static uint8_t connectPhys = BLE_GAP_LE_PHY_CODED_MASK | BLE_GAP_LE_PHY_1M_MASK /*| BLE_GAP_LE_PHY_2M_MASK */ ; static uint8_t connectPhys = BLE_GAP_LE_PHY_CODED_MASK | BLE_GAP_LE_PHY_1M_MASK /*| BLE_GAP_LE_PHY_2M_MASK */;
/* Define a class to handle the callbacks for client connection events */ /** Define a class to handle the callbacks for client connection events */
class ClientCallbacks : public NimBLEClientCallbacks { class ClientCallbacks : public NimBLEClientCallbacks {
void onConnect(NimBLEClient* pClient) { void onConnect(NimBLEClient* pClient) override { printf("Connected\n"); };
printf("Connected\n");
};
void onDisconnect(NimBLEClient* pClient, int reason) { void onDisconnect(NimBLEClient* pClient, int reason) override {
printf("%s Disconnected, reason = %d - Starting scan\n", printf("%s Disconnected, reason = %d - Starting scan\n", pClient->getPeerAddress().toString().c_str(), reason);
pClient->getPeerAddress().toString().c_str(), reason); NimBLEDevice::getScan()->start(scanTimeMs);
NimBLEDevice::getScan()->start(scanTime); }
}; } clientCallbacks;
};
/** Define a class to handle the callbacks when advertisements are received */
/* Define a class to handle the callbacks when advertisements are received */ class scanCallbacks : public NimBLEScanCallbacks {
class scanCallbacks: public NimBLEScanCallbacks { void onResult(const NimBLEAdvertisedDevice* advertisedDevice) override {
void onResult(NimBLEAdvertisedDevice* advertisedDevice) {
printf("Advertised Device found: %s\n", advertisedDevice->toString().c_str()); printf("Advertised Device found: %s\n", advertisedDevice->toString().c_str());
if(advertisedDevice->isAdvertisingService(NimBLEUUID("ABCD"))) if (advertisedDevice->isAdvertisingService(NimBLEUUID("ABCD"))) {
{
printf("Found Our Service\n"); printf("Found Our Service\n");
/* Ready to connect now */
doConnect = true; doConnect = true;
/* Save the device reference in a global for the client to use*/ /** Save the device reference in a global for the client to use*/
advDevice = advertisedDevice; advDevice = advertisedDevice;
/* stop scan before connecting */ /** stop scan before connecting */
NimBLEDevice::getScan()->stop(); NimBLEDevice::getScan()->stop();
} }
} }
/** Callback to process the results of the completed scan or restart it */ /** Callback to process the results of the completed scan or restart it */
void onScanEnd(NimBLEScanResults results) { void onScanEnd(const NimBLEScanResults& results, int rc) override { printf("Scan Ended\n"); }
printf("Scan Ended\n"); } scanCallbacks;
}
};
/** Handles the provisioning of clients and connects / interfaces with the server */
/* Handles the provisioning of clients and connects / interfaces with the server */
bool connectToServer() { bool connectToServer() {
NimBLEClient* pClient = nullptr; NimBLEClient* pClient = nullptr;
pClient = NimBLEDevice::createClient(); pClient = NimBLEDevice::createClient();
pClient->setClientCallbacks(new ClientCallbacks, false); pClient->setClientCallbacks(&clientCallbacks, false);
/* Set the PHY's to use for this connection. This is a bitmask that represents the PHY's: /**
* Set the PHY's to use for this connection. This is a bitmask that represents the PHY's:
* * 0x01 BLE_GAP_LE_PHY_1M_MASK * * 0x01 BLE_GAP_LE_PHY_1M_MASK
* * 0x02 BLE_GAP_LE_PHY_2M_MASK * * 0x02 BLE_GAP_LE_PHY_2M_MASK
* * 0x04 BLE_GAP_LE_PHY_CODED_MASK * * 0x04 BLE_GAP_LE_PHY_CODED_MASK
@ -77,27 +68,22 @@ bool connectToServer() {
pClient->setConnectTimeout(10 * 1000); pClient->setConnectTimeout(10 * 1000);
if (!pClient->connect(advDevice)) { if (!pClient->connect(advDevice)) {
/* Created a client but failed to connect, don't need to keep it as it has no data */ /** Created a client but failed to connect, don't need to keep it as it has no data */
NimBLEDevice::deleteClient(pClient); NimBLEDevice::deleteClient(pClient);
printf("Failed to connect, deleted client\n"); printf("Failed to connect, deleted client\n");
return false; return false;
} }
printf("Connected to: %s RSSI: %d\n", printf("Connected to: %s RSSI: %d\n", pClient->getPeerAddress().toString().c_str(), pClient->getRssi());
pClient->getPeerAddress().toString().c_str(),
pClient->getRssi());
/* Now we can read/write/subscribe the charateristics of the services we are interested in */ /** Now we can read/write/subscribe the characteristics of the services we are interested in */
NimBLERemoteService* pSvc = nullptr; NimBLERemoteService* pSvc = nullptr;
NimBLERemoteCharacteristic* pChr = nullptr; NimBLERemoteCharacteristic* pChr = nullptr;
pSvc = pClient->getService(SERVICE_UUID); pSvc = pClient->getService(SERVICE_UUID);
if (pSvc) { if (pSvc) {
pChr = pSvc->getCharacteristic(CHARACTERISTIC_UUID); pChr = pSvc->getCharacteristic(CHARACTERISTIC_UUID);
if (pChr) { if (pChr) {
// Read the value of the characteristic.
if (pChr->canRead()) { if (pChr->canRead()) {
std::string value = pChr->readValue(); std::string value = pChr->readValue();
printf("Characteristic value: %s\n", value.c_str()); printf("Characteristic value: %s\n", value.c_str());
@ -113,11 +99,37 @@ bool connectToServer() {
return true; return true;
} }
void connectTask (void * parameter){ extern "C" void app_main(void) {
/* Loop here until we find a device we want to connect to */ printf("Starting NimBLE Client\n");
/** Initialize NimBLE and set the device name */
NimBLEDevice::init("NimBLE Extended Client");
/** Create aNimBLE Scan instance and set the callbacks for scan events */
NimBLEScan* pScan = NimBLEDevice::getScan();
pScan->setScanCallbacks(&scanCallbacks);
/** Set scan interval (how often) and window (how long) in milliseconds */
pScan->setInterval(97);
pScan->setWindow(67);
/**
* Active scan will gather scan response data from advertisers
* but will use more energy from both devices
*/
pScan->setActiveScan(true);
/**
* Start scanning for advertisers for the scan time specified (in milliseconds) 0 = forever
* Optional callback for when scanning stops.
*/
pScan->start(scanTimeMs);
printf("Scanning for peripherals\n");
/** Loop here until we find a device we want to connect to */
for (;;) { for (;;) {
if (doConnect) { if (doConnect) {
/* Found a device we want to connect to, do it now */
if (connectToServer()) { if (connectToServer()) {
printf("Success!, scanning for more!\n"); printf("Success!, scanning for more!\n");
} else { } else {
@ -125,39 +137,8 @@ void connectTask (void * parameter){
} }
doConnect = false; doConnect = false;
NimBLEDevice::getScan()->start(scanTime); NimBLEDevice::getScan()->start(scanTimeMs);
} }
vTaskDelay(pdMS_TO_TICKS(10)); vTaskDelay(pdMS_TO_TICKS(10));
} }
vTaskDelete(NULL);
}
void app_main (void) {
printf("Starting NimBLE Client\n");
/* Create a task to handle connecting to peers */
xTaskCreate(connectTask, "connectTask", 5000, NULL, 1, NULL);
/* Initialize NimBLE, no device name specified as we are not advertising */
NimBLEDevice::init("");
NimBLEScan* pScan = NimBLEDevice::getScan();
/* create a callback that gets called when advertisers are found */
pScan->setScanCallbacks(new scanCallbacks());
/* Set scan interval (how often) and window (how long) in milliseconds */
pScan->setInterval(97);
pScan->setWindow(67);
/* Active scan will gather scan response data from advertisers
* but will use more energy from both devices
*/
pScan->setActiveScan(true);
/* Start scanning for advertisers for the scan time specified (in milliseconds) 0 = forever
* Optional callback for when scanning stops.
*/
pScan->start(scanTime);
printf("Scanning for peripherals\n");
} }

View File

@ -3,5 +3,4 @@
cmake_minimum_required(VERSION 3.5) cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake) include($ENV{IDF_PATH}/tools/cmake/project.cmake)
set(SUPPORTED_TARGETS esp32) project(NimBLE_extended_scan)
project(BLE_uart)

View File

@ -0,0 +1,69 @@
/**
* NimBLE Extended Scanner Demo:
*
* Demonstrates the Bluetooth 5.x scanning capabilities of the NimBLE library.
*
* Created: on November 28, 2024
* Author: H2zero
*/
#include <NimBLEDevice.h>
static uint32_t scanTimeMs = 10 * 1000; // In milliseconds, 0 = scan forever
static NimBLEScan::Phy scanPhy = NimBLEScan::Phy::SCAN_ALL;
/** Define a class to handle the callbacks when advertisements are received */
class ScanCallbacks : public NimBLEScanCallbacks {
void onResult(const NimBLEAdvertisedDevice* advertisedDevice) {
printf("Advertised Device found: %s\n PHY1: %d\n PHY2: %d\n",
advertisedDevice->toString().c_str(),
advertisedDevice->getPrimaryPhy(),
advertisedDevice->getSecondaryPhy());
}
/** Callback to process the results of the completed scan or restart it */
void onScanEnd(const NimBLEScanResults& scanResults, int reason) {
printf("Scan Ended, reason: %d; found %d devices\n", reason, scanResults.getCount());
/** Try Different PHY's */
switch (scanPhy) {
case NimBLEScan::Phy::SCAN_ALL:
printf("Scanning only 1M PHY\n");
scanPhy = NimBLEScan::Phy::SCAN_1M;
break;
case NimBLEScan::Phy::SCAN_1M:
printf("Scanning only CODED PHY\n");
scanPhy = NimBLEScan::Phy::SCAN_CODED;
break;
case NimBLEScan::Phy::SCAN_CODED:
printf("Scanning all PHY's\n");
scanPhy = NimBLEScan::Phy::SCAN_ALL;
break;
}
NimBLEScan* pScan = NimBLEDevice::getScan();
pScan->setPhy(scanPhy);
pScan->start(scanTimeMs);
}
} scanCallbacks;
extern "C" void app_main(void) {
printf("Starting Extended Scanner\n");
/** Initialize NimBLE and set the device name */
NimBLEDevice::init("NimBLE Extended Scanner");
NimBLEScan* pScan = NimBLEDevice::getScan();
/** Set the callbacks that the scanner will call on events. */
pScan->setScanCallbacks(&scanCallbacks);
/** Use active scanning to obtain scan response data from advertisers */
pScan->setActiveScan(true);
/** Set the initial PHY's to scan on, default is SCAN_ALL */
pScan->setPhy(scanPhy);
/** Start scanning for scanTimeMs */
pScan->start(scanTimeMs);
printf("Scanning for peripherals\n");
}

View File

@ -3,5 +3,4 @@
cmake_minimum_required(VERSION 3.5) cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake) include($ENV{IDF_PATH}/tools/cmake/project.cmake)
set(SUPPORTED_TARGETS esp32c3 esp32s3)
project(NimBLE_extended_server) project(NimBLE_extended_server)

View File

@ -1,3 +0,0 @@
PROJECT_NAME := NimBLE_extended_server
include $(IDF_PATH)/make/project.mk

View File

@ -1,4 +0,0 @@
#
# "main" pseudo-component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)

View File

@ -1,4 +1,5 @@
/** NimBLE Extended Server Demo: /**
* NimBLE Extended Server Demo:
* *
* Demonstrates the Bluetooth 5.x extended advertising capabilities. * Demonstrates the Bluetooth 5.x extended advertising capabilities.
* *
@ -9,55 +10,52 @@
* *
* Created: on April 2 2022 * Created: on April 2 2022
* Author: H2zero * Author: H2zero
* */
*/
#include "NimBLEDevice.h" #include <NimBLEDevice.h>
#include "esp_sleep.h" #include <esp_sleep.h>
extern "C" void app_main(void);
#define SERVICE_UUID "ABCD" #define SERVICE_UUID "ABCD"
#define CHARACTERISTIC_UUID "1234" #define CHARACTERISTIC_UUID "1234"
/* Time in milliseconds to advertise */ /** Time in milliseconds to advertise */
static uint32_t advTime = 5000; static uint32_t advTime = 5000;
/* Time to sleep between advertisements */ /** Time to sleep between advertisements */
static uint32_t sleepSeconds = 20; static uint32_t sleepSeconds = 20;
/* Primary PHY used for advertising, can be one of BLE_HCI_LE_PHY_1M or BLE_HCI_LE_PHY_CODED */ /** Primary PHY used for advertising, can be one of BLE_HCI_LE_PHY_1M or BLE_HCI_LE_PHY_CODED */
static uint8_t primaryPhy = BLE_HCI_LE_PHY_CODED; static uint8_t primaryPhy = BLE_HCI_LE_PHY_CODED;
/* Secondary PHY used for advertising and connecting, /**
* can be one of BLE_HCI_LE_PHY_1M, BLE_HCI_LE_PHY_2M or BLE_HCI_LE_PHY_CODED * Secondary PHY used for advertising and connecting,
* can be one of BLE_HCI_LE_PHY_1M, BLE_HCI_LE_PHY_2M or BLE_HCI_LE_PHY_CODED
*/ */
static uint8_t secondaryPhy = BLE_HCI_LE_PHY_1M; static uint8_t secondaryPhy = BLE_HCI_LE_PHY_1M;
/** Handler class for server events */
/* Handler class for server events */ class ServerCallbacks : public NimBLEServerCallbacks {
class ServerCallbacks: public NimBLEServerCallbacks { void onConnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo) override {
void onConnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo) {
printf("Client connected:: %s\n", connInfo.getAddress().toString().c_str()); printf("Client connected:: %s\n", connInfo.getAddress().toString().c_str());
}; }
void onDisconnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo, int reason) { void onDisconnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo, int reason) override {
printf("Client disconnected - sleeping for %" PRIu32" seconds\n", sleepSeconds); printf("Client disconnected - sleeping for %" PRIu32 " seconds\n", sleepSeconds);
esp_deep_sleep_start(); esp_deep_sleep_start();
}; }
}; } serverCallbacks;
/* Callback class to handle advertising events */ /** Callback class to handle advertising events */
class advertisingCallbacks: public NimBLEExtAdvertisingCallbacks { class AdvertisingCallbacks : public NimBLEExtAdvertisingCallbacks {
void onStopped(NimBLEExtAdvertising* pAdv, int reason, uint8_t inst_id) { void onStopped(NimBLEExtAdvertising* pAdv, int reason, uint8_t instId) override {
/* Check the reason advertising stopped, don't sleep if client is connecting */ /* Check the reason advertising stopped, don't sleep if client is connecting */
printf("Advertising instance %u stopped\n", inst_id); printf("Advertising instance %u stopped\n", instId);
switch (reason) { switch (reason) {
case 0: case 0:
printf("Client connecting\n"); printf("Client connecting\n");
return; return;
case BLE_HS_ETIMEOUT: case BLE_HS_ETIMEOUT:
printf("Time expired - sleeping for %" PRIu32" seconds\n", sleepSeconds); printf("Time expired - sleeping for %" PRIu32 " seconds\n", sleepSeconds);
break; break;
default: default:
break; break;
@ -65,66 +63,67 @@ class advertisingCallbacks: public NimBLEExtAdvertisingCallbacks {
esp_deep_sleep_start(); esp_deep_sleep_start();
} }
}; } advertisingCallbacks;
void app_main (void) { extern "C" void app_main(void) {
/** Initialize NimBLE and set the device name */
NimBLEDevice::init("Extended advertiser"); NimBLEDevice::init("Extended advertiser");
/* Create the server and add the services/characteristics/descriptors */ /** Create the server and add the services/characteristics/descriptors */
NimBLEServer *pServer = NimBLEDevice::createServer(); NimBLEServer* pServer = NimBLEDevice::createServer();
pServer->setCallbacks(new ServerCallbacks); pServer->setCallbacks(&serverCallbacks);
NimBLEService *pService = pServer->createService(SERVICE_UUID); NimBLEService* pService = pServer->createService(SERVICE_UUID);
NimBLECharacteristic *pCharacteristic = pService->createCharacteristic(CHARACTERISTIC_UUID, NimBLECharacteristic* pCharacteristic =
NIMBLE_PROPERTY::READ | pService->createCharacteristic(CHARACTERISTIC_UUID,
NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::NOTIFY);
NIMBLE_PROPERTY::NOTIFY);
pCharacteristic->setValue("Hello World"); pCharacteristic->setValue("Hello World");
/* Start the services */ /** Start the service */
pService->start(); pService->start();
/* /**
* Create an extended advertisement with the instance ID 0 and set the PHY's. * Create an extended advertisement with the instance ID 0 and set the PHY's.
* Multiple instances can be added as long as the instance ID is incremented. * Multiple instances can be added as long as the instance ID is incremented.
*/ */
NimBLEExtAdvertisement extAdv(primaryPhy, secondaryPhy); NimBLEExtAdvertisement extAdv(primaryPhy, secondaryPhy);
/* Set the advertisement as connectable */ /** Set the advertisement as connectable */
extAdv.setConnectable(true); extAdv.setConnectable(true);
/* As per Bluetooth specification, extended advertising cannot be both scannable and connectable */ /** As per Bluetooth specification, extended advertising cannot be both scannable and connectable */
extAdv.setScannable(false); // The default is false, set here for demonstration. extAdv.setScannable(false); // The default is false, set here for demonstration.
/* Extended advertising allows for 251 bytes (minus header bytes ~20) in a single advertisement or up to 1650 if chained */ /** Extended advertising allows for 251 bytes (minus header bytes ~20) in a single advertisement or up to 1650 if chained */
extAdv.setServiceData(NimBLEUUID(SERVICE_UUID), std::string("Extended Advertising Demo.\r\n" extAdv.setServiceData(NimBLEUUID(SERVICE_UUID),
"Extended advertising allows for " std::string("Extended Advertising Demo.\r\n"
"251 bytes of data in a single advertisement,\r\n" "Extended advertising allows for "
"or up to 1650 bytes with chaining.\r\n" "251 bytes of data in a single advertisement,\r\n"
"This example message is 226 bytes long " "or up to 1650 bytes with chaining.\r\n"
"and is using CODED_PHY for long range.")); "This example message is 226 bytes long "
"and is using CODED_PHY for long range."));
extAdv.setCompleteServices16({NimBLEUUID(SERVICE_UUID)}); extAdv.setName("Extended advertiser");
/* When extended advertising is enabled `NimBLEDevice::getAdvertising` returns a pointer to `NimBLEExtAdvertising */ /** When extended advertising is enabled `NimBLEDevice::getAdvertising` returns a pointer to `NimBLEExtAdvertising */
NimBLEExtAdvertising* pAdvertising = NimBLEDevice::getAdvertising(); NimBLEExtAdvertising* pAdvertising = NimBLEDevice::getAdvertising();
/* Set the callbacks for advertising events */ /** Set the callbacks for advertising events */
pAdvertising->setCallbacks(new advertisingCallbacks); pAdvertising->setCallbacks(&advertisingCallbacks);
/* /**
* NimBLEExtAdvertising::setInstanceData takes the instance ID and * NimBLEExtAdvertising::setInstanceData takes the instance ID and
* a reference to a `NimBLEExtAdvertisement` object. This sets the data * a reference to a `NimBLEExtAdvertisement` object. This sets the data
* that will be advertised for this instance ID, returns true if successful. * that will be advertised for this instance ID, returns true if successful.
* *
* Note: It is safe to create the advertisement as a local variable if setInstanceData * Note: It is safe to create the advertisement as a local variable if setInstanceData
* is called before exiting the code block as the data will be copied. * is called before exiting the code block as the data will be copied.
*/ */
if (pAdvertising->setInstanceData(0, extAdv)) { if (pAdvertising->setInstanceData(0, extAdv)) {
/* /**
* `NimBLEExtAdvertising::start` takes the advertisement instance ID to start * NimBLEExtAdvertising::start takes the advertisement instance ID to start
* and a duration in milliseconds or a max number of advertisements to send (or both). * and a duration in milliseconds or a max number of advertisements to send (or both).
*/ */
if (pAdvertising->start(0, advTime)) { if (pAdvertising->start(0, advTime)) {
printf("Started advertising\n"); printf("Started advertising\n");
@ -132,7 +131,7 @@ void app_main (void) {
printf("Failed to start advertising\n"); printf("Failed to start advertising\n");
} }
} else { } else {
printf("Failed to register advertisment data\n"); printf("Failed to register advertisement data\n");
} }
esp_sleep_enable_timer_wakeup(sleepSeconds * 1000000); esp_sleep_enable_timer_wakeup(sleepSeconds * 1000000);

View File

@ -3,5 +3,4 @@
cmake_minimum_required(VERSION 3.5) cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake) include($ENV{IDF_PATH}/tools/cmake/project.cmake)
set(SUPPORTED_TARGETS esp32c3 esp32s3)
project(NimBLE_multi_advertiser) project(NimBLE_multi_advertiser)

View File

@ -1,3 +0,0 @@
PROJECT_NAME := NimBLE_multi_advertiser
include $(IDF_PATH)/make/project.mk

View File

@ -1,4 +0,0 @@
#
# "main" pseudo-component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)

View File

@ -1,4 +1,5 @@
/** NimBLE Multi Advertiser Demo: /**
* NimBLE Multi Advertiser Demo:
* *
* Demonstrates the Bluetooth 5.x extended advertising capabilities. * Demonstrates the Bluetooth 5.x extended advertising capabilities.
* *
@ -9,59 +10,56 @@
* *
* Created: on April 9 2022 * Created: on April 9 2022
* Author: H2zero * Author: H2zero
* */
*/
#include "NimBLEDevice.h" #include <NimBLEDevice.h>
#include "esp_sleep.h" #include <esp_sleep.h>
extern "C" void app_main(void);
#define SERVICE_UUID "ABCD" #define SERVICE_UUID "ABCD"
#define CHARACTERISTIC_UUID "1234" #define CHARACTERISTIC_UUID "1234"
/* Time in milliseconds to advertise */ /** Time in milliseconds to advertise */
static uint32_t advTime = 5000; static uint32_t advTime = 5000;
/* Time to sleep between advertisements */ /** Time to sleep between advertisements */
static uint32_t sleepTime = 20; static uint32_t sleepTime = 20;
/* Primary PHY used for advertising, can be one of BLE_HCI_LE_PHY_1M or BLE_HCI_LE_PHY_CODED */ /** Primary PHY used for advertising, can be one of BLE_HCI_LE_PHY_1M or BLE_HCI_LE_PHY_CODED */
static uint8_t primaryPhy = BLE_HCI_LE_PHY_CODED; static uint8_t primaryPhy = BLE_HCI_LE_PHY_CODED;
/* Secondary PHY used for advertising and connecting, /**
* can be one of BLE_HCI_LE_PHY_1M, BLE_HCI_LE_PHY_2M or BLE_HCI_LE_PHY_CODED * Secondary PHY used for advertising and connecting,
* can be one of BLE_HCI_LE_PHY_1M, BLE_HCI_LE_PHY_2M or BLE_HCI_LE_PHY_CODED
*/ */
static uint8_t secondaryPhy = BLE_HCI_LE_PHY_1M; static uint8_t secondaryPhy = BLE_HCI_LE_PHY_1M;
/** Handler class for server events */
/* Handler class for server events */ class ServerCallbacks : public NimBLEServerCallbacks {
class ServerCallbacks: public NimBLEServerCallbacks { void onConnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo) override {
void onConnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo) {
printf("Client connected: %s\n", connInfo.getAddress().toString().c_str()); printf("Client connected: %s\n", connInfo.getAddress().toString().c_str());
}; }
void onDisconnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo, int reason) { void onDisconnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo, int reason) override {
printf("Client disconnected\n"); printf("Client disconnected\n");
// if still advertising we won't sleep yet. // if still advertising we won't sleep yet.
if (!pServer->getAdvertising()->isAdvertising()) { if (!pServer->getAdvertising()->isAdvertising()) {
printf("Sleeping for %" PRIu32" seconds\n", sleepTime); printf("Sleeping for %" PRIu32 " seconds\n", sleepTime);
esp_deep_sleep_start(); esp_deep_sleep_start();
} }
}; }
}; } serverCallbacks;
/* Callback class to handle advertising events */ /** Callback class to handle advertising events */
class advCallbacks: public NimBLEExtAdvertisingCallbacks { class AdvCallbacks : public NimBLEExtAdvertisingCallbacks {
void onStopped(NimBLEExtAdvertising* pAdv, int reason, uint8_t inst_id) { void onStopped(NimBLEExtAdvertising* pAdv, int reason, uint8_t instId) override {
/* Check the reason advertising stopped, don't sleep if client is connecting */ /* Check the reason advertising stopped, don't sleep if client is connecting */
printf("Advertising instance %u stopped\n", inst_id); printf("Advertising instance %u stopped\n", instId);
switch (reason) { switch (reason) {
case 0: case 0:
printf(" client connecting\n"); printf(" client connecting\n");
return; return;
case BLE_HS_ETIMEOUT: case BLE_HS_ETIMEOUT:
printf("Time expired - sleeping for %" PRIu32" seconds\n", sleepTime); printf("Time expired - sleeping for %" PRIu32 " seconds\n", sleepTime);
break; break;
default: default:
break; break;
@ -72,90 +70,90 @@ class advCallbacks: public NimBLEExtAdvertisingCallbacks {
bool m_updatedSR = false; bool m_updatedSR = false;
void onScanRequest(NimBLEExtAdvertising* pAdv, uint8_t inst_id, NimBLEAddress addr) { void onScanRequest(NimBLEExtAdvertising* pAdv, uint8_t instId, NimBLEAddress addr) override {
printf("Scan request for instance %u\n", inst_id); printf("Scan request for instance %u\n", instId);
// if the data has already been updated we don't need to change it again. // if the data has already been updated we don't need to change it again.
if (!m_updatedSR) { if (!m_updatedSR) {
printf("Updating scan data\n"); printf("Updating scan data\n");
NimBLEExtAdvertisement sr; NimBLEExtAdvertisement sr;
sr.setServiceData(NimBLEUUID(SERVICE_UUID), std::string("Hello from scan response!")); sr.setServiceData(NimBLEUUID(SERVICE_UUID), std::string("Hello from scan response!"));
pAdv->setScanResponseData(inst_id, sr); pAdv->setScanResponseData(instId, sr);
m_updatedSR = true; m_updatedSR = true;
} }
} }
}; } advCallbacks;
void app_main (void) { extern "C" void app_main(void) {
/** Initialize NimBLE and set the device name */
NimBLEDevice::init("Multi advertiser"); NimBLEDevice::init("Multi advertiser");
/* Create a server for our legacy advertiser */ /** Create a server for our legacy advertiser */
NimBLEServer *pServer = NimBLEDevice::createServer(); NimBLEServer* pServer = NimBLEDevice::createServer();
pServer->setCallbacks(new ServerCallbacks); pServer->setCallbacks(&serverCallbacks);
NimBLEService *pService = pServer->createService(SERVICE_UUID); NimBLEService* pService = pServer->createService(SERVICE_UUID);
NimBLECharacteristic *pCharacteristic = pService->createCharacteristic(CHARACTERISTIC_UUID, NimBLECharacteristic* pCharacteristic =
NIMBLE_PROPERTY::READ | pService->createCharacteristic(CHARACTERISTIC_UUID,
NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::NOTIFY);
NIMBLE_PROPERTY::NOTIFY);
pCharacteristic->setValue("Hello World"); pCharacteristic->setValue("Hello World");
/* Start the service */ /** Start the service */
pService->start(); pService->start();
/* Create our multi advertising instances */ /** Create our multi advertising instances */
// extended scannable instance advertising on coded and 1m PHY's. /** extended scannable instance advertising on coded and 1m PHY's. */
NimBLEExtAdvertisement extScannable(primaryPhy, secondaryPhy); NimBLEExtAdvertisement extScannable(primaryPhy, secondaryPhy);
// Legacy advertising as a connectable device. /** Legacy advertising as a connectable device. */
NimBLEExtAdvertisement legacyConnectable; NimBLEExtAdvertisement legacyConnectable;
// Optional scan response data. /** Optional scan response data. */
NimBLEExtAdvertisement legacyScanResponse; NimBLEExtAdvertisement legacyScanResponse;
/* As per Bluetooth specification, extended advertising cannot be both scannable and connectable */ /** As per Bluetooth specification, extended advertising cannot be both scannable and connectable */
extScannable.setScannable(true); extScannable.setScannable(true);
extScannable.setConnectable(false); extScannable.setConnectable(false);
/* Set the initial data */ /** Set the initial data */
extScannable.setServiceData(NimBLEUUID(SERVICE_UUID), std::string("Scan me!")); extScannable.setServiceData(NimBLEUUID(SERVICE_UUID), std::string("Scan me!"));
/* enable the scan response callback, we will use this to update the data. */ /** Enable the scan response callback, we will use this to update the data. */
extScannable.enableScanRequestCallback(true); extScannable.enableScanRequestCallback(true);
/* Optional custom address for this advertisment. */ /** Optional custom address for this advertisment. */
legacyConnectable.setAddress(NimBLEAddress("DE:AD:BE:EF:BA:AD")); legacyConnectable.setAddress(NimBLEAddress("DE:AD:BE:EF:BA:AD"));
/* Set the advertising data. */ /** Set the advertising data. */
legacyConnectable.setName("Legacy"); legacyConnectable.setName("Legacy");
legacyConnectable.setCompleteServices16({NimBLEUUID(SERVICE_UUID)}); legacyConnectable.setCompleteServices16({NimBLEUUID(SERVICE_UUID)});
/* Set the legacy and connectable flags. */ /** Set the legacy and connectable flags. */
legacyConnectable.setLegacyAdvertising(true); legacyConnectable.setLegacyAdvertising(true);
legacyConnectable.setConnectable(true); legacyConnectable.setConnectable(true);
/* Put some data in the scan response if desired. */ /** Put some data in the scan response if desired. */
legacyScanResponse.setServiceData(NimBLEUUID(SERVICE_UUID), "Legacy SR"); legacyScanResponse.setServiceData(NimBLEUUID(SERVICE_UUID), "Legacy SR");
/* Get the advertising ready */ /** Get the advertising ready */
NimBLEExtAdvertising* pAdvertising = NimBLEDevice::getAdvertising(); NimBLEExtAdvertising* pAdvertising = NimBLEDevice::getAdvertising();
/* Set the callbacks to handle advertising events */ /** Set the callbacks to handle advertising events */
pAdvertising->setCallbacks(new advCallbacks); pAdvertising->setCallbacks(&advCallbacks);
/* Set instance data. /**
* Up to 5 instances can be used if configured in menuconfig, instance 0 is always available. * Set instance data.
* Up to 5 instances can be used if configured in menuconfig, instance 0 is always available.
* *
* We will set the extended scannable data on instance 0 and the legacy data on instance 1. * We will set the extended scannable data on instance 0 and the legacy data on instance 1.
* Note that the legacy scan response data needs to be set to the same instance (1). * Note that the legacy scan response data needs to be set to the same instance (1).
*/ */
if (pAdvertising->setInstanceData( 0, extScannable ) && if (pAdvertising->setInstanceData(0, extScannable) && pAdvertising->setInstanceData(1, legacyConnectable) &&
pAdvertising->setInstanceData( 1, legacyConnectable ) && pAdvertising->setScanResponseData(1, legacyScanResponse)) {
pAdvertising->setScanResponseData( 1, legacyScanResponse )) { /**
/* * NimBLEExtAdvertising::start takes the advertisement instance ID to start
* `NimBLEExtAdvertising::start` takes the advertisement instance ID to start * and a duration in milliseconds or a max number of advertisements to send (or both).
* and a duration in milliseconds or a max number of advertisements to send (or both).
*/ */
if (pAdvertising->start(0, advTime) && pAdvertising->start(1, advTime)) { if (pAdvertising->start(0, advTime) && pAdvertising->start(1, advTime)) {
printf("Started advertising\n"); printf("Started advertising\n");
@ -163,7 +161,7 @@ void app_main (void) {
printf("Failed to start advertising\n"); printf("Failed to start advertising\n");
} }
} else { } else {
printf("Failed to register advertisment data\n"); printf("Failed to register advertisement data\n");
} }
esp_sleep_enable_timer_wakeup(sleepTime * 1000000); esp_sleep_enable_timer_wakeup(sleepTime * 1000000);

View File

@ -3,5 +3,4 @@
cmake_minimum_required(VERSION 3.5) cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake) include($ENV{IDF_PATH}/tools/cmake/project.cmake)
set(SUPPORTED_TARGETS esp32) project(Continuous_scan)
project(BLE_scan)

View File

@ -0,0 +1,46 @@
/**
* Continuous Scan Example
*
* This example demonstrates how to continuously scan for BLE devices.
* When devices are found the onDiscovered and onResults callbacks will be called with the device data.
* The scan will not store the results, only the callbacks will be used
* When the scan timeout is reached the onScanEnd callback will be called and the scan will be restarted.
* This will clear the duplicate cache in the controller and allow the same devices to be reported again.
*
* Created: on March 24 2020
* Author: H2zero
*/
#include <NimBLEDevice.h>
static constexpr uint32_t scanTime = 30 * 1000; // 30 seconds scan time.
class scanCallbacks : public NimBLEScanCallbacks {
/** Initial discovery, advertisement data only. */
void onDiscovered(const NimBLEAdvertisedDevice* advertisedDevice) override {
printf("Discovered Device: %s\n", advertisedDevice->toString().c_str());
}
/**
* If active scanning the result here will have the scan response data.
* If not active scanning then this will be the same as onDiscovered.
*/
void onResult(const NimBLEAdvertisedDevice* advertisedDevice) override {
printf("Device result: %s\n", advertisedDevice->toString().c_str());
}
void onScanEnd(const NimBLEScanResults& results, int reason) override {
printf("Scan ended reason = %d; restarting scan\n", reason);
NimBLEDevice::getScan()->start(scanTime, false, true);
}
} scanCallbacks;
extern "C" void app_main() {
NimBLEDevice::init(""); // Initialize the device, you can specify a device name if you want.
NimBLEScan* pBLEScan = NimBLEDevice::getScan(); // Create the scan object.
pBLEScan->setScanCallbacks(&scanCallbacks, false); // Set the callback for when devices are discovered, no duplicates.
pBLEScan->setActiveScan(true); // Set active scanning, this will get more data from the advertiser.
pBLEScan->setMaxResults(0); // Do not store the scan results, use callback only.
pBLEScan->start(scanTime, false, true); // duration, not a continuation of last scan, restart to get all devices again.
printf("Scanning...\n");
}

View File

@ -0,0 +1,5 @@
.vscode
build
sdkconfig
sdkconfig.old
dependencies.lock

View File

@ -3,5 +3,5 @@
cmake_minimum_required(VERSION 3.5) cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake) include($ENV{IDF_PATH}/tools/cmake/project.cmake)
set(SUPPORTED_TARGETS esp32) set(SUPPORTED_TARGETS esp32 esp32s3 esp32c3 esp32c6)
project(NimBLE_server_get_client_name) project(L2CAP_client)

View File

@ -1,3 +1,3 @@
PROJECT_NAME := BLE_scan PROJECT_NAME := L2CAP_client
include $(IDF_PATH)/make/project.mk include $(IDF_PATH)/make/project.mk

View File

@ -0,0 +1,3 @@
dependencies:
local/esp-nimble-cpp:
path: ../../../../../esp-nimble-cpp/

View File

@ -0,0 +1,165 @@
#include <NimBLEDevice.h>
// See the following for generating UUIDs:
// https://www.uuidgenerator.net/
// The remote service we wish to connect to.
static BLEUUID serviceUUID("dcbc7255-1e9e-49a0-a360-b0430b6c6905");
// The characteristic of the remote service we are interested in.
static BLEUUID charUUID("371a55c8-f251-4ad2-90b3-c7c195b049be");
#define L2CAP_CHANNEL 150
#define L2CAP_MTU 5000
const BLEAdvertisedDevice* theDevice = NULL;
BLEClient* theClient = NULL;
BLEL2CAPChannel* theChannel = NULL;
size_t bytesSent = 0;
size_t bytesReceived = 0;
class L2CAPChannelCallbacks: public BLEL2CAPChannelCallbacks {
public:
void onConnect(NimBLEL2CAPChannel* channel) {
printf("L2CAP connection established\n");
}
void onMTUChange(NimBLEL2CAPChannel* channel, uint16_t mtu) {
printf("L2CAP MTU changed to %d\n", mtu);
}
void onRead(NimBLEL2CAPChannel* channel, std::vector<uint8_t>& data) {
printf("L2CAP read %d bytes\n", data.size());
}
void onDisconnect(NimBLEL2CAPChannel* channel) {
printf("L2CAP disconnected\n");
}
};
class MyClientCallbacks: public BLEClientCallbacks {
void onConnect(BLEClient* pClient) {
printf("GAP connected\n");
pClient->setDataLen(251);
theChannel = BLEL2CAPChannel::connect(pClient, L2CAP_CHANNEL, L2CAP_MTU, new L2CAPChannelCallbacks());
}
void onDisconnect(BLEClient* pClient, int reason) {
printf("GAP disconnected (reason: %d)\n", reason);
theDevice = NULL;
theChannel = NULL;
vTaskDelay(1000 / portTICK_PERIOD_MS);
BLEDevice::getScan()->start(5 * 1000, true);
}
};
class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks {
void onResult(const BLEAdvertisedDevice* advertisedDevice) {
if (theDevice) { return; }
printf("BLE Advertised Device found: %s\n", advertisedDevice->toString().c_str());
if (!advertisedDevice->haveServiceUUID()) { return; }
if (!advertisedDevice->isAdvertisingService(serviceUUID)) { return; }
printf("Found the device we're interested in!\n");
BLEDevice::getScan()->stop();
// Hand over the device to the other task
theDevice = advertisedDevice;
}
};
void connectTask(void *pvParameters) {
uint8_t sequenceNumber = 0;
while (true) {
if (!theDevice) {
vTaskDelay(1000 / portTICK_PERIOD_MS);
continue;
}
if (!theClient) {
theClient = BLEDevice::createClient();
theClient->setConnectionParams(6, 6, 0, 42);
auto callbacks = new MyClientCallbacks();
theClient->setClientCallbacks(callbacks);
auto success = theClient->connect(theDevice);
if (!success) {
printf("Error: Could not connect to device\n");
break;
}
vTaskDelay(2000 / portTICK_PERIOD_MS);
continue;
}
if (!theChannel) {
printf("l2cap channel not initialized\n");
vTaskDelay(2000 / portTICK_PERIOD_MS);
continue;
}
if (!theChannel->isConnected()) {
printf("l2cap channel not connected\n");
vTaskDelay(2000 / portTICK_PERIOD_MS);
continue;
}
while (theChannel->isConnected()) {
/*
static auto initialDelay = true;
if (initialDelay) {
printf("Waiting gracefully 3 seconds before sending data\n");
vTaskDelay(3000 / portTICK_PERIOD_MS);
initialDelay = false;
};
*/
std::vector<uint8_t> data(5000, sequenceNumber++);
if (theChannel->write(data)) {
bytesSent += data.size();
} else {
printf("failed to send!\n");
abort();
}
}
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
}
extern "C"
void app_main(void) {
printf("Starting L2CAP client example\n");
xTaskCreate(connectTask, "connectTask", 5000, NULL, 1, NULL);
BLEDevice::init("L2CAP-Client");
BLEDevice::setMTU(BLE_ATT_MTU_MAX);
auto scan = BLEDevice::getScan();
auto callbacks = new MyAdvertisedDeviceCallbacks();
scan->setScanCallbacks(callbacks);
scan->setInterval(1349);
scan->setWindow(449);
scan->setActiveScan(true);
scan->start(25 * 1000, false);
int numberOfSeconds = 0;
while (bytesSent == 0) {
vTaskDelay(10 / portTICK_PERIOD_MS);
}
while (true) {
vTaskDelay(1000 / portTICK_PERIOD_MS);
int bytesSentPerSeconds = bytesSent / ++numberOfSeconds;
printf("Bandwidth: %d b/sec = %d KB/sec\n", bytesSentPerSeconds, bytesSentPerSeconds / 1024);
}
}

View File

@ -0,0 +1,7 @@
# The following lines of boilerplate have to be in your project's
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
set(SUPPORTED_TARGETS esp32 esp32s3 esp32c3 esp32c6)
project(L2CAP_server)

View File

@ -1,3 +1,3 @@
PROJECT_NAME := BLE_server PROJECT_NAME := L2CAP_server
include $(IDF_PATH)/make/project.mk include $(IDF_PATH)/make/project.mk

View File

@ -0,0 +1,3 @@
dependencies:
local/esp-nimble-cpp:
path: ../../../../../esp-nimble-cpp/

View File

@ -0,0 +1,90 @@
#include <NimBLEDevice.h>
// See the following for generating UUIDs:
// https://www.uuidgenerator.net/
#define SERVICE_UUID "dcbc7255-1e9e-49a0-a360-b0430b6c6905"
#define CHARACTERISTIC_UUID "371a55c8-f251-4ad2-90b3-c7c195b049be"
#define L2CAP_CHANNEL 150
#define L2CAP_MTU 5000
class GATTCallbacks: public BLEServerCallbacks {
public:
void onConnect(BLEServer* pServer, BLEConnInfo& info) {
/// Booster #1
pServer->setDataLen(info.getConnHandle(), 251);
/// Booster #2 (especially for Apple devices)
BLEDevice::getServer()->updateConnParams(info.getConnHandle(), 12, 12, 0, 200);
}
};
class L2CAPChannelCallbacks: public BLEL2CAPChannelCallbacks {
public:
bool connected = false;
size_t numberOfReceivedBytes;
uint8_t nextSequenceNumber;
public:
void onConnect(NimBLEL2CAPChannel* channel) {
printf("L2CAP connection established\n");
connected = true;
numberOfReceivedBytes = nextSequenceNumber = 0;
}
void onRead(NimBLEL2CAPChannel* channel, std::vector<uint8_t>& data) {
numberOfReceivedBytes += data.size();
size_t sequenceNumber = data[0];
printf("L2CAP read %d bytes w/ sequence number %d", data.size(), sequenceNumber);
if (sequenceNumber != nextSequenceNumber) {
printf("(wrong sequence number %d, expected %d)\n", sequenceNumber, nextSequenceNumber);
} else {
printf("\n");
nextSequenceNumber++;
}
}
void onDisconnect(NimBLEL2CAPChannel* channel) {
printf("L2CAP disconnected\n");
connected = false;
}
};
extern "C"
void app_main(void) {
printf("Starting L2CAP server example [%lu free] [%lu min]\n", esp_get_free_heap_size(), esp_get_minimum_free_heap_size());
BLEDevice::init("L2CAP-Server");
BLEDevice::setMTU(BLE_ATT_MTU_MAX);
auto cocServer = BLEDevice::createL2CAPServer();
auto l2capChannelCallbacks = new L2CAPChannelCallbacks();
auto channel = cocServer->createService(L2CAP_CHANNEL, L2CAP_MTU, l2capChannelCallbacks);
auto server = BLEDevice::createServer();
server->setCallbacks(new GATTCallbacks());
auto service = server->createService(SERVICE_UUID);
auto characteristic = service->createCharacteristic(CHARACTERISTIC_UUID, NIMBLE_PROPERTY::READ);
characteristic->setValue(L2CAP_CHANNEL);
service->start();
auto advertising = BLEDevice::getAdvertising();
advertising->addServiceUUID(SERVICE_UUID);
advertising->enableScanResponse(true);
BLEDevice::startAdvertising();
printf("Server waiting for connection requests [%lu free] [%lu min]\n", esp_get_free_heap_size(), esp_get_minimum_free_heap_size());
// Wait until transfer actually starts...
while (!l2capChannelCallbacks->numberOfReceivedBytes) {
vTaskDelay(10 / portTICK_PERIOD_MS);
}
printf("\n\n\n");
int numberOfSeconds = 0;
while (true) {
vTaskDelay(1000 / portTICK_PERIOD_MS);
if (!l2capChannelCallbacks->connected) { continue; }
int bps = l2capChannelCallbacks->numberOfReceivedBytes / ++numberOfSeconds;
printf("Bandwidth: %d b/sec = %d KB/sec [%lu free] [%lu min]\n", bps, bps / 1024, esp_get_free_heap_size(), esp_get_minimum_free_heap_size());
}
}

View File

@ -3,5 +3,4 @@
cmake_minimum_required(VERSION 3.5) cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake) include($ENV{IDF_PATH}/tools/cmake/project.cmake)
set(SUPPORTED_TARGETS esp32) project(NimBLE_Async_Client)
project(BLE_client)

View File

@ -0,0 +1,83 @@
/**
* NimBLE_Async_client Demo:
*
* Demonstrates asynchronous client operations.
*
* Created: on November 4, 2024
* Author: H2zero
*/
#include <NimBLEDevice.h>
static constexpr uint32_t scanTimeMs = 5 * 1000;
class ClientCallbacks : public NimBLEClientCallbacks {
void onConnect(NimBLEClient* pClient) override {
printf("Connected to: %s\n", pClient->getPeerAddress().toString().c_str());
}
void onDisconnect(NimBLEClient* pClient, int reason) override {
printf("%s Disconnected, reason = %d - Starting scan\n", pClient->getPeerAddress().toString().c_str(), reason);
NimBLEDevice::getScan()->start(scanTimeMs);
}
} clientCallbacks;
class ScanCallbacks : public NimBLEScanCallbacks {
void onResult(const NimBLEAdvertisedDevice* advertisedDevice) override {
printf("Advertised Device found: %s\n", advertisedDevice->toString().c_str());
if (advertisedDevice->haveName() && advertisedDevice->getName() == "NimBLE-Server") {
printf("Found Our Device\n");
/** Async connections can be made directly in the scan callbacks */
auto pClient = NimBLEDevice::getDisconnectedClient();
if (!pClient) {
pClient = NimBLEDevice::createClient(advertisedDevice->getAddress());
if (!pClient) {
printf("Failed to create client\n");
return;
}
}
pClient->setClientCallbacks(&clientCallbacks, false);
if (!pClient->connect(true, true, false)) { // delete attributes, async connect, no MTU exchange
NimBLEDevice::deleteClient(pClient);
printf("Failed to connect\n");
return;
}
}
}
void onScanEnd(const NimBLEScanResults& results, int reason) override {
printf("Scan Ended\n");
NimBLEDevice::getScan()->start(scanTimeMs);
}
} scanCallbacks;
extern "C" void app_main(void) {
printf("Starting NimBLE Async Client\n");
NimBLEDevice::init("Async-Client");
NimBLEDevice::setPower(3); /** +3db */
NimBLEScan* pScan = NimBLEDevice::getScan();
pScan->setScanCallbacks(&scanCallbacks);
pScan->setInterval(45);
pScan->setWindow(15);
pScan->setActiveScan(true);
pScan->start(scanTimeMs);
for (;;) {
vTaskDelay(pdMS_TO_TICKS(1000));
auto pClients = NimBLEDevice::getConnectedClients();
if (!pClients.size()) {
continue;
}
for (auto& pClient : pClients) {
printf("%s\n", pClient->toString().c_str());
NimBLEDevice::deleteClient(pClient);
}
NimBLEDevice::getScan()->start(scanTimeMs);
}
}

View File

@ -3,5 +3,4 @@
cmake_minimum_required(VERSION 3.5) cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake) include($ENV{IDF_PATH}/tools/cmake/project.cmake)
set(SUPPORTED_TARGETS esp32)
project(NimBLE_Client) project(NimBLE_Client)

View File

@ -0,0 +1,304 @@
/** NimBLE_Client Demo:
*
* Demonstrates many of the available features of the NimBLE client library.
*
* Created: on March 24 2020
* Author: H2zero
*/
#include <NimBLEDevice.h>
static const NimBLEAdvertisedDevice* advDevice;
static bool doConnect = false;
static uint32_t scanTimeMs = 5000; /** scan time in milliseconds, 0 = scan forever */
/** None of these are required as they will be handled by the library with defaults. **
** Remove as you see fit for your needs */
class ClientCallbacks : public NimBLEClientCallbacks {
void onConnect(NimBLEClient* pClient) override { printf("Connected\n"); }
void onDisconnect(NimBLEClient* pClient, int reason) override {
printf("%s Disconnected, reason = %d - Starting scan\n", pClient->getPeerAddress().toString().c_str(), reason);
NimBLEDevice::getScan()->start(scanTimeMs, false, true);
}
/********************* Security handled here *********************/
void onPassKeyEntry(NimBLEConnInfo& connInfo) override {
printf("Server Passkey Entry\n");
/**
* This should prompt the user to enter the passkey displayed
* on the peer device.
*/
NimBLEDevice::injectPassKey(connInfo, 123456);
}
void onConfirmPasskey(NimBLEConnInfo& connInfo, uint32_t pass_key) override {
printf("The passkey YES/NO number: %" PRIu32 "\n", pass_key);
/** Inject false if passkeys don't match. */
NimBLEDevice::injectConfirmPasskey(connInfo, true);
}
/** Pairing process complete, we can check the results in connInfo */
void onAuthenticationComplete(NimBLEConnInfo& connInfo) override {
if (!connInfo.isEncrypted()) {
printf("Encrypt connection failed - disconnecting\n");
/** Find the client with the connection handle provided in connInfo */
NimBLEDevice::getClientByHandle(connInfo.getConnHandle())->disconnect();
return;
}
}
} clientCallbacks;
/** Define a class to handle the callbacks when scan events are received */
class ScanCallbacks : public NimBLEScanCallbacks {
void onResult(const NimBLEAdvertisedDevice* advertisedDevice) override {
printf("Advertised Device found: %s\n", advertisedDevice->toString().c_str());
if (advertisedDevice->isAdvertisingService(NimBLEUUID("DEAD"))) {
printf("Found Our Service\n");
/** stop scan before connecting */
NimBLEDevice::getScan()->stop();
/** Save the device reference in a global for the client to use*/
advDevice = advertisedDevice;
/** Ready to connect now */
doConnect = true;
}
}
/** Callback to process the results of the completed scan or restart it */
void onScanEnd(const NimBLEScanResults& results, int reason) override {
printf("Scan Ended, reason: %d, device count: %d; Restarting scan\n", reason, results.getCount());
NimBLEDevice::getScan()->start(scanTimeMs, false, true);
}
} scanCallbacks;
/** Notification / Indication receiving handler callback */
void notifyCB(NimBLERemoteCharacteristic* pRemoteCharacteristic, uint8_t* pData, size_t length, bool isNotify) {
std::string str = (isNotify == true) ? "Notification" : "Indication";
str += " from ";
str += pRemoteCharacteristic->getClient()->getPeerAddress().toString();
str += ": Service = " + pRemoteCharacteristic->getRemoteService()->getUUID().toString();
str += ", Characteristic = " + pRemoteCharacteristic->getUUID().toString();
str += ", Value = " + std::string((char*)pData, length);
printf("%s\n", str.c_str());
}
/** Handles the provisioning of clients and connects / interfaces with the server */
bool connectToServer() {
NimBLEClient* pClient = nullptr;
/** Check if we have a client we should reuse first **/
if (NimBLEDevice::getCreatedClientCount()) {
/**
* Special case when we already know this device, we send false as the
* second argument in connect() to prevent refreshing the service database.
* This saves considerable time and power.
*/
pClient = NimBLEDevice::getClientByPeerAddress(advDevice->getAddress());
if (pClient) {
if (!pClient->connect(advDevice, false)) {
printf("Reconnect failed\n");
return false;
}
printf("Reconnected client\n");
} else {
/**
* We don't already have a client that knows this device,
* check for a client that is disconnected that we can use.
*/
pClient = NimBLEDevice::getDisconnectedClient();
}
}
/** No client to reuse? Create a new one. */
if (!pClient) {
if (NimBLEDevice::getCreatedClientCount() >= NIMBLE_MAX_CONNECTIONS) {
printf("Max clients reached - no more connections available\n");
return false;
}
pClient = NimBLEDevice::createClient();
printf("New client created\n");
pClient->setClientCallbacks(&clientCallbacks, false);
/**
* Set initial connection parameters:
* These settings are safe for 3 clients to connect reliably, can go faster if you have less
* connections. Timeout should be a multiple of the interval, minimum is 100ms.
* Min interval: 12 * 1.25ms = 15, Max interval: 12 * 1.25ms = 15, 0 latency, 150 * 10ms = 1500ms timeout
*/
pClient->setConnectionParams(12, 12, 0, 150);
/** Set how long we are willing to wait for the connection to complete (milliseconds), default is 30000. */
pClient->setConnectTimeout(5 * 1000);
if (!pClient->connect(advDevice)) {
/** Created a client but failed to connect, don't need to keep it as it has no data */
NimBLEDevice::deleteClient(pClient);
printf("Failed to connect, deleted client\n");
return false;
}
}
if (!pClient->isConnected()) {
if (!pClient->connect(advDevice)) {
printf("Failed to connect\n");
return false;
}
}
printf("Connected to: %s RSSI: %d\n", pClient->getPeerAddress().toString().c_str(), pClient->getRssi());
/** Now we can read/write/subscribe the characteristics of the services we are interested in */
NimBLERemoteService* pSvc = nullptr;
NimBLERemoteCharacteristic* pChr = nullptr;
NimBLERemoteDescriptor* pDsc = nullptr;
pSvc = pClient->getService("DEAD");
if (pSvc) {
pChr = pSvc->getCharacteristic("BEEF");
}
if (pChr) {
if (pChr->canRead()) {
printf("%s Value: %s\n", pChr->getUUID().toString().c_str(), pChr->readValue().c_str());
}
if (pChr->canWrite()) {
if (pChr->writeValue("Tasty")) {
printf("Wrote new value to: %s\n", pChr->getUUID().toString().c_str());
} else {
pClient->disconnect();
return false;
}
if (pChr->canRead()) {
printf("The value of: %s is now: %s\n", pChr->getUUID().toString().c_str(), pChr->readValue().c_str());
}
}
if (pChr->canNotify()) {
if (!pChr->subscribe(true, notifyCB)) {
pClient->disconnect();
return false;
}
} else if (pChr->canIndicate()) {
/** Send false as first argument to subscribe to indications instead of notifications */
if (!pChr->subscribe(false, notifyCB)) {
pClient->disconnect();
return false;
}
}
} else {
printf("DEAD service not found.\n");
}
pSvc = pClient->getService("BAAD");
if (pSvc) {
pChr = pSvc->getCharacteristic("F00D");
if (pChr) {
if (pChr->canRead()) {
printf("%s Value: %s\n", pChr->getUUID().toString().c_str(), pChr->readValue().c_str());
}
pDsc = pChr->getDescriptor(NimBLEUUID("C01D"));
if (pDsc) {
printf("Descriptor: %s Value: %s\n", pDsc->getUUID().toString().c_str(), pDsc->readValue().c_str());
}
if (pChr->canWrite()) {
if (pChr->writeValue("No tip!")) {
printf("Wrote new value to: %s\n", pChr->getUUID().toString().c_str());
} else {
pClient->disconnect();
return false;
}
if (pChr->canRead()) {
printf("The value of: %s is now: %s\n", pChr->getUUID().toString().c_str(), pChr->readValue().c_str());
}
}
if (pChr->canNotify()) {
if (!pChr->subscribe(true, notifyCB)) {
pClient->disconnect();
return false;
}
} else if (pChr->canIndicate()) {
/** Send false as first argument to subscribe to indications instead of notifications */
if (!pChr->subscribe(false, notifyCB)) {
pClient->disconnect();
return false;
}
}
}
} else {
printf("BAAD service not found.\n");
}
printf("Done with this device!\n");
return true;
}
extern "C" void app_main(void) {
printf("Starting NimBLE Client\n");
/** Initialize NimBLE and set the device name */
NimBLEDevice::init("NimBLE-Client");
/**
* Set the IO capabilities of the device, each option will trigger a different pairing method.
* BLE_HS_IO_KEYBOARD_ONLY - Passkey pairing
* BLE_HS_IO_DISPLAY_YESNO - Numeric comparison pairing
* BLE_HS_IO_NO_INPUT_OUTPUT - DEFAULT setting - just works pairing
*/
// NimBLEDevice::setSecurityIOCap(BLE_HS_IO_KEYBOARD_ONLY); // use passkey
// NimBLEDevice::setSecurityIOCap(BLE_HS_IO_DISPLAY_YESNO); //use numeric comparison
/**
* 2 different ways to set security - both calls achieve the same result.
* no bonding, no man in the middle protection, BLE secure connections.
* These are the default values, only shown here for demonstration.
*/
// NimBLEDevice::setSecurityAuth(false, false, true);
NimBLEDevice::setSecurityAuth(/*BLE_SM_PAIR_AUTHREQ_BOND | BLE_SM_PAIR_AUTHREQ_MITM |*/ BLE_SM_PAIR_AUTHREQ_SC);
/** Optional: set the transmit power */
NimBLEDevice::setPower(3); /** 3dbm */
NimBLEScan* pScan = NimBLEDevice::getScan();
/** Set the callbacks to call when scan events occur, no duplicates */
pScan->setScanCallbacks(&scanCallbacks, false);
/** Set scan interval (how often) and window (how long) in milliseconds */
pScan->setInterval(100);
pScan->setWindow(100);
/**
* Active scan will gather scan response data from advertisers
* but will use more energy from both devices
*/
pScan->setActiveScan(true);
/** Start scanning for advertisers */
pScan->start(scanTimeMs);
printf("Scanning for peripherals\n");
/** Loop here until we find a device we want to connect to */
for (;;) {
vTaskDelay(10 / portTICK_PERIOD_MS);
if (doConnect) {
doConnect = false;
/** Found a device we want to connect to, do it now */
if (connectToServer()) {
printf("Success! we should now be getting notifications, scanning for more!\n");
} else {
printf("Failed to connect, starting scan\n");
}
NimBLEDevice::getScan()->start(scanTimeMs, false, true);
}
}
}

View File

@ -3,5 +3,4 @@
cmake_minimum_required(VERSION 3.5) cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake) include($ENV{IDF_PATH}/tools/cmake/project.cmake)
set(SUPPORTED_TARGETS esp32)
project(NimBLE_Server) project(NimBLE_Server)

View File

@ -1,224 +1,186 @@
/** NimBLE_Server Demo: /**
* NimBLE_Server Demo:
* *
* Demonstrates many of the available features of the NimBLE server library. * Demonstrates many of the available features of the NimBLE server library.
* *
* Created: on March 22 2020 * Created: on March 22 2020
* Author: H2zero * Author: H2zero
* */
*/
#include "NimBLEDevice.h"
#include "NimBLELog.h"
#include <stdio.h> #include <NimBLEDevice.h>
extern "C" {void app_main(void);}
static NimBLEServer* pServer; static NimBLEServer* pServer;
/** None of these are required as they will be handled by the library with defaults. ** /** None of these are required as they will be handled by the library with defaults. **
** Remove as you see fit for your needs */ ** Remove as you see fit for your needs */
class ServerCallbacks: public NimBLEServerCallbacks { class ServerCallbacks : public NimBLEServerCallbacks {
void onConnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo) { void onConnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo) override {
printf("Client address: %s\n", connInfo.getAddress().toString().c_str()); printf("Client address: %s\n", connInfo.getAddress().toString().c_str());
/** We can use the connection handle here to ask for different connection parameters. /**
* We can use the connection handle here to ask for different connection parameters.
* Args: connection handle, min connection interval, max connection interval * Args: connection handle, min connection interval, max connection interval
* latency, supervision timeout. * latency, supervision timeout.
* Units; Min/Max Intervals: 1.25 millisecond increments. * Units; Min/Max Intervals: 1.25 millisecond increments.
* Latency: number of intervals allowed to skip. * Latency: number of intervals allowed to skip.
* Timeout: 10 millisecond increments, try for 3x interval time for best results. * Timeout: 10 millisecond increments.
*/ */
pServer->updateConnParams(connInfo.getConnHandle(), 24, 48, 0, 18); pServer->updateConnParams(connInfo.getConnHandle(), 24, 48, 0, 180);
}; }
void onDisconnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo, int reason) { void onDisconnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo, int reason) override {
printf("Client disconnected - start advertising\n"); printf("Client disconnected - start advertising\n");
NimBLEDevice::startAdvertising(); NimBLEDevice::startAdvertising();
}; }
void onMTUChange(uint16_t MTU, NimBLEConnInfo& connInfo) { void onMTUChange(uint16_t MTU, NimBLEConnInfo& connInfo) override {
printf("MTU updated: %u for connection ID: %u\n", MTU, connInfo.getConnHandle()); printf("MTU updated: %u for connection ID: %u\n", MTU, connInfo.getConnHandle());
pServer->updateConnParams(connInfo.getConnHandle(), 24, 48, 0, 60); }
};
/********************* Security handled here ********************** /********************* Security handled here *********************/
****** Note: these are the same return values as defaults ********/ uint32_t onPassKeyDisplay() override {
uint32_t onPassKeyDisplay(){
printf("Server Passkey Display\n"); printf("Server Passkey Display\n");
/** This should return a random 6 digit number for security /**
* This should return a random 6 digit number for security
* or make your own static passkey as done here. * or make your own static passkey as done here.
*/ */
return 123456; return 123456;
}; }
void onConfirmPIN(const NimBLEConnInfo& connInfo, uint32_t pass_key){ void onConfirmPassKey(NimBLEConnInfo& connInfo, uint32_t pass_key) override {
printf("The passkey YES/NO number: %" PRIu32 "\n", pass_key); printf("The passkey YES/NO number: %" PRIu32 "\n", pass_key);
/** Inject false if passkeys don't match. */ /** Inject false if passkeys don't match. */
NimBLEDevice::injectConfirmPIN(connInfo, true); NimBLEDevice::injectConfirmPasskey(connInfo, true);
}; }
void onAuthenticationComplete(const NimBLEConnInfo& connInfo){ void onAuthenticationComplete(NimBLEConnInfo& connInfo) override {
/** Check that encryption was successful, if not we disconnect the client */ /** Check that encryption was successful, if not we disconnect the client */
if(!connInfo.isEncrypted()) { if (!connInfo.isEncrypted()) {
NimBLEDevice::getServer()->disconnect(connInfo.getConnHandle()); NimBLEDevice::getServer()->disconnect(connInfo.getConnHandle());
printf("Encrypt connection failed - disconnecting client\n"); printf("Encrypt connection failed - disconnecting client\n");
return; return;
} }
printf("Starting BLE work!");
}; printf("Secured connection to: %s\n", connInfo.getAddress().toString().c_str());
}; }
} serverCallbacks;
/** Handler class for characteristic actions */ /** Handler class for characteristic actions */
class CharacteristicCallbacks: public NimBLECharacteristicCallbacks { class CharacteristicCallbacks : public NimBLECharacteristicCallbacks {
void onRead(NimBLECharacteristic* pCharacteristic, NimBLEConnInfo& connInfo) { void onRead(NimBLECharacteristic* pCharacteristic, NimBLEConnInfo& connInfo) override {
printf("%s : onRead(), value: %s\n", printf("%s : onRead(), value: %s\n",
pCharacteristic->getUUID().toString().c_str(), pCharacteristic->getUUID().toString().c_str(),
pCharacteristic->getValue().c_str()); pCharacteristic->getValue().c_str());
} }
void onWrite(NimBLECharacteristic* pCharacteristic, NimBLEConnInfo& connInfo) { void onWrite(NimBLECharacteristic* pCharacteristic, NimBLEConnInfo& connInfo) override {
printf("%s : onWrite(), value: %s\n", printf("%s : onWrite(), value: %s\n",
pCharacteristic->getUUID().toString().c_str(), pCharacteristic->getUUID().toString().c_str(),
pCharacteristic->getValue().c_str()); pCharacteristic->getValue().c_str());
} }
/** Called before notification or indication is sent,
* the value can be changed here before sending if desired.
*/
void onNotify(NimBLECharacteristic* pCharacteristic) {
printf("Sending notification to clients\n");
}
/** /**
* The value returned in code is the NimBLE host return code. * The value returned in code is the NimBLE host return code.
*/ */
void onStatus(NimBLECharacteristic* pCharacteristic, int code) { void onStatus(NimBLECharacteristic* pCharacteristic, int code) override {
printf("Notification/Indication return code: %d, %s\n", printf("Notification/Indication return code: %d, %s\n", code, NimBLEUtils::returnCodeToString(code));
code, NimBLEUtils::returnCodeToString(code));
} }
void onSubscribe(NimBLECharacteristic* pCharacteristic, NimBLEConnInfo& connInfo, uint16_t subValue) { /** Peer subscribed to notifications/indications */
std::string str = "Client ID: "; void onSubscribe(NimBLECharacteristic* pCharacteristic, NimBLEConnInfo& connInfo, uint16_t subValue) override {
str += connInfo.getConnHandle(); std::string str = "Client ID: ";
str += " Address: "; str += connInfo.getConnHandle();
str += connInfo.getAddress().toString(); str += " Address: ";
if(subValue == 0) { str += connInfo.getAddress().toString();
if (subValue == 0) {
str += " Unsubscribed to "; str += " Unsubscribed to ";
}else if(subValue == 1) { } else if (subValue == 1) {
str += " Subscribed to notfications for "; str += " Subscribed to notifications for ";
} else if(subValue == 2) { } else if (subValue == 2) {
str += " Subscribed to indications for "; str += " Subscribed to indications for ";
} else if(subValue == 3) { } else if (subValue == 3) {
str += " Subscribed to notifications and indications for "; str += " Subscribed to notifications and indications for ";
} }
str += std::string(pCharacteristic->getUUID()); str += std::string(pCharacteristic->getUUID());
printf("%s\n", str.c_str()); printf("%s\n", str.c_str());
} }
}; } chrCallbacks;
/** Handler class for descriptor actions */ /** Handler class for descriptor actions */
class DescriptorCallbacks : public NimBLEDescriptorCallbacks { class DescriptorCallbacks : public NimBLEDescriptorCallbacks {
void onWrite(NimBLEDescriptor* pDescriptor, NimBLEConnInfo& connInfo) { void onWrite(NimBLEDescriptor* pDescriptor, NimBLEConnInfo& connInfo) override {
std::string dscVal = pDescriptor->getValue(); std::string dscVal = pDescriptor->getValue();
printf("Descriptor witten value: %s\n", dscVal.c_str()); printf("Descriptor written value: %s\n", dscVal.c_str());
};
void onRead(NimBLEDescriptor* pDescriptor, NimBLEConnInfo& connInfo) {
printf("%s Descriptor read\n", pDescriptor->getUUID().toString().c_str());
};;
};
/** Define callback instances globally to use for multiple Charateristics \ Descriptors */
static DescriptorCallbacks dscCallbacks;
static CharacteristicCallbacks chrCallbacks;
void notifyTask(void * parameter){
for(;;) {
if(pServer->getConnectedCount()) {
NimBLEService* pSvc = pServer->getServiceByUUID("BAAD");
if(pSvc) {
NimBLECharacteristic* pChr = pSvc->getCharacteristic("F00D");
if(pChr) {
pChr->notify(true);
}
}
}
vTaskDelay(2000/portTICK_PERIOD_MS);
} }
vTaskDelete(NULL); void onRead(NimBLEDescriptor* pDescriptor, NimBLEConnInfo& connInfo) override {
} printf("%s Descriptor read\n", pDescriptor->getUUID().toString().c_str());
}
} dscCallbacks;
void app_main(void) { extern "C" void app_main(void) {
printf("Starting NimBLE Server\n"); printf("Starting NimBLE Server\n");
/** sets device name */ /** Initialize NimBLE and set the device name */
NimBLEDevice::init("NimBLE"); NimBLEDevice::init("NimBLE");
/** Set the IO capabilities of the device, each option will trigger a different pairing method. /**
* Set the IO capabilities of the device, each option will trigger a different pairing method.
* BLE_HS_IO_DISPLAY_ONLY - Passkey pairing * BLE_HS_IO_DISPLAY_ONLY - Passkey pairing
* BLE_HS_IO_DISPLAY_YESNO - Numeric comparison pairing * BLE_HS_IO_DISPLAY_YESNO - Numeric comparison pairing
* BLE_HS_IO_NO_INPUT_OUTPUT - DEFAULT setting - just works pairing * BLE_HS_IO_NO_INPUT_OUTPUT - DEFAULT setting - just works pairing
*/ */
//NimBLEDevice::setSecurityIOCap(BLE_HS_IO_DISPLAY_ONLY); // use passkey // NimBLEDevice::setSecurityIOCap(BLE_HS_IO_DISPLAY_ONLY); // use passkey
//NimBLEDevice::setSecurityIOCap(BLE_HS_IO_DISPLAY_YESNO); //use numeric comparison // NimBLEDevice::setSecurityIOCap(BLE_HS_IO_DISPLAY_YESNO); //use numeric comparison
/** 2 different ways to set security - both calls achieve the same result. /**
* no bonding, no man in the middle protection, secure connections. * 2 different ways to set security - both calls achieve the same result.
* no bonding, no man in the middle protection, BLE secure connections.
* *
* These are the default values, only shown here for demonstration. * These are the default values, only shown here for demonstration.
*/ */
//NimBLEDevice::setSecurityAuth(false, false, true); // NimBLEDevice::setSecurityAuth(false, false, true);
NimBLEDevice::setSecurityAuth(/*BLE_SM_PAIR_AUTHREQ_BOND | BLE_SM_PAIR_AUTHREQ_MITM |*/ BLE_SM_PAIR_AUTHREQ_SC); NimBLEDevice::setSecurityAuth(/*BLE_SM_PAIR_AUTHREQ_BOND | BLE_SM_PAIR_AUTHREQ_MITM |*/ BLE_SM_PAIR_AUTHREQ_SC);
pServer = NimBLEDevice::createServer(); pServer = NimBLEDevice::createServer();
pServer->setCallbacks(new ServerCallbacks()); pServer->setCallbacks(&serverCallbacks);
NimBLEService* pDeadService = pServer->createService("DEAD"); NimBLEService* pDeadService = pServer->createService("DEAD");
NimBLECharacteristic* pBeefCharacteristic = pDeadService->createCharacteristic( NimBLECharacteristic* pBeefCharacteristic =
"BEEF", pDeadService->createCharacteristic("BEEF",
NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE |
NIMBLE_PROPERTY::WRITE | /** Require a secure connection for read and write access */
/** Require a secure connection for read and write access */ NIMBLE_PROPERTY::READ_ENC | // only allow reading if paired / encrypted
NIMBLE_PROPERTY::READ_ENC | // only allow reading if paired / encrypted NIMBLE_PROPERTY::WRITE_ENC // only allow writing if paired / encrypted
NIMBLE_PROPERTY::WRITE_ENC // only allow writing if paired / encrypted );
);
pBeefCharacteristic->setValue("Burger"); pBeefCharacteristic->setValue("Burger");
pBeefCharacteristic->setCallbacks(&chrCallbacks); pBeefCharacteristic->setCallbacks(&chrCallbacks);
/** 2902 and 2904 descriptors are a special case, when createDescriptor is called with /**
* 2902 and 2904 descriptors are a special case, when createDescriptor is called with
* either of those uuid's it will create the associated class with the correct properties * either of those uuid's it will create the associated class with the correct properties
* and sizes. However we must cast the returned reference to the correct type as the method * and sizes. However we must cast the returned reference to the correct type as the method
* only returns a pointer to the base NimBLEDescriptor class. * only returns a pointer to the base NimBLEDescriptor class.
*/ */
NimBLE2904* pBeef2904 = (NimBLE2904*)pBeefCharacteristic->createDescriptor("2904"); NimBLE2904* pBeef2904 = pBeefCharacteristic->create2904();
pBeef2904->setFormat(NimBLE2904::FORMAT_UTF8); pBeef2904->setFormat(NimBLE2904::FORMAT_UTF8);
pBeef2904->setCallbacks(&dscCallbacks); pBeef2904->setCallbacks(&dscCallbacks);
NimBLEService* pBaadService = pServer->createService("BAAD");
NimBLEService* pBaadService = pServer->createService("BAAD"); NimBLECharacteristic* pFoodCharacteristic =
NimBLECharacteristic* pFoodCharacteristic = pBaadService->createCharacteristic( pBaadService->createCharacteristic("F00D", NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::NOTIFY);
"F00D",
NIMBLE_PROPERTY::READ |
NIMBLE_PROPERTY::WRITE |
NIMBLE_PROPERTY::NOTIFY
);
pFoodCharacteristic->setValue("Fries"); pFoodCharacteristic->setValue("Fries");
pFoodCharacteristic->setCallbacks(&chrCallbacks); pFoodCharacteristic->setCallbacks(&chrCallbacks);
/** Custom descriptor: Arguments are UUID, Properties, max length in bytes of the value */ /** Custom descriptor: Arguments are UUID, Properties, max length of the value in bytes */
NimBLEDescriptor* pC01Ddsc = pFoodCharacteristic->createDescriptor( NimBLEDescriptor* pC01Ddsc =
"C01D", pFoodCharacteristic->createDescriptor("C01D",
NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::WRITE_ENC,
NIMBLE_PROPERTY::WRITE| 20);
NIMBLE_PROPERTY::WRITE_ENC, // only allow writing if paired / encrypted
20
);
pC01Ddsc->setValue("Send it back!"); pC01Ddsc->setValue("Send it back!");
pC01Ddsc->setCallbacks(&dscCallbacks); pC01Ddsc->setCallbacks(&dscCallbacks);
@ -226,17 +188,31 @@ void app_main(void) {
pDeadService->start(); pDeadService->start();
pBaadService->start(); pBaadService->start();
/** Create an advertising instance and add the services to the advertised data */
NimBLEAdvertising* pAdvertising = NimBLEDevice::getAdvertising(); NimBLEAdvertising* pAdvertising = NimBLEDevice::getAdvertising();
/** Add the services to the advertisment data **/ pAdvertising->setName("NimBLE-Server");
pAdvertising->addServiceUUID(pDeadService->getUUID()); pAdvertising->addServiceUUID(pDeadService->getUUID());
pAdvertising->addServiceUUID(pBaadService->getUUID()); pAdvertising->addServiceUUID(pBaadService->getUUID());
/** If your device is battery powered you may consider setting scan response /**
* If your device is battery powered you may consider setting scan response
* to false as it will extend battery life at the expense of less data sent. * to false as it will extend battery life at the expense of less data sent.
*/ */
pAdvertising->setScanResponse(true); pAdvertising->enableScanResponse(true);
pAdvertising->start(); pAdvertising->start();
printf("Advertising Started\n"); printf("Advertising Started\n");
xTaskCreate(notifyTask, "notifyTask", 5000, NULL, 1, NULL); /** Loop here and send notifications to connected peers */
for (;;) {
if (pServer->getConnectedCount()) {
NimBLEService* pSvc = pServer->getServiceByUUID("BAAD");
if (pSvc) {
NimBLECharacteristic* pChr = pSvc->getCharacteristic("F00D");
if (pChr) {
pChr->notify();
}
}
}
vTaskDelay(2000 / portTICK_PERIOD_MS);
}
} }

View File

@ -1,83 +0,0 @@
/** NimBLE_server_get_client_name
*
* Demonstrates 2 ways for the server to read the device name from the connected client.
*
* Created: on June 24 2024
* Author: H2zero
*
*/
#include <NimBLEDevice.h>
// See the following for generating UUIDs:
// https://www.uuidgenerator.net/
#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8"
#define ENC_CHARACTERISTIC_UUID "9551f35b-8d91-42e4-8f7e-1358dfe272dc"
NimBLEServer* pServer;
class ServerCallbacks : public NimBLEServerCallbacks {
// Same as before but now includes the name parameter
void onConnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo, std::string& name) override {
printf("Client address: %s Name: %s\n", connInfo.getAddress().toString().c_str(), name.c_str());
}
// Same as before but now includes the name parameter
void onAuthenticationComplete(const NimBLEConnInfo& connInfo, const std::string& name) override {
if (!connInfo.isEncrypted()) {
NimBLEDevice::getServer()->disconnect(connInfo.getConnHandle());
printf("Encrypt connection failed - disconnecting client\n");
return;
}
printf("Encrypted Client address: %s Name: %s\n", connInfo.getAddress().toString().c_str(), name.c_str());
}
};
extern "C" void app_main(void) {
printf("Starting BLE Server!\n");
NimBLEDevice::init("Connect to me!");
NimBLEDevice::setSecurityAuth(true, false, true); // Enable bonding to see full name on phones.
pServer = NimBLEDevice::createServer();
NimBLEService* pService = pServer->createService(SERVICE_UUID);
NimBLECharacteristic* pCharacteristic =
pService->createCharacteristic(CHARACTERISTIC_UUID, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE);
pCharacteristic->setValue("Hello World says NimBLE!");
NimBLECharacteristic* pEncCharacteristic = pService->createCharacteristic(
ENC_CHARACTERISTIC_UUID,
(NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::READ_ENC | NIMBLE_PROPERTY::WRITE_ENC));
pEncCharacteristic->setValue("Hello World says NimBLE Encrypted");
pService->start();
pServer->setCallbacks(new ServerCallbacks());
pServer->getPeerNameOnConnect(true); // Setting this will enable the onConnect callback that provides the name.
BLEAdvertising* pAdvertising = NimBLEDevice::getAdvertising();
pAdvertising->addServiceUUID(SERVICE_UUID);
pAdvertising->setScanResponse(true);
pAdvertising->start();
printf("Advertising started, connect with your phone.\n");
while (true) {
auto clientCount = pServer->getConnectedCount();
if (clientCount) {
printf("Connected clients:\n");
for (auto i = 0; i < clientCount; ++i) {
NimBLEConnInfo peerInfo = pServer->getPeerInfo(i);
printf("Client address: %s Name: %s\n", peerInfo.getAddress().toString().c_str(),
// This function blocks until the name is retrieved, so cannot be used in callback functions.
pServer->getPeerName(peerInfo).c_str());
}
}
vTaskDelay(pdMS_TO_TICKS(10000));
}
}

View File

@ -1,3 +0,0 @@
PROJECT_NAME := BLE_client
include $(IDF_PATH)/make/project.mk

View File

@ -1,4 +0,0 @@
#
# "main" pseudo-component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)

View File

@ -1,214 +0,0 @@
/**
* A BLE client example that is rich in capabilities.
* There is a lot new capabilities implemented.
* author unknown
* updated by chegewara
* updated for NimBLE by H2zero
*/
/** NimBLE differences highlighted in comment blocks **/
/*******original********
#include "BLEDevice.h"
***********************/
#include "NimBLEDevice.h"
extern "C"{void app_main(void);}
// The remote service we wish to connect to.
static BLEUUID serviceUUID("4fafc201-1fb5-459e-8fcc-c5c9c331914b");
// The characteristic of the remote service we are interested in.
static BLEUUID charUUID("beb5483e-36e1-4688-b7f5-ea07361b26a8");
static bool doConnect = false;
static bool connected = false;
static bool doScan = false;
static BLERemoteCharacteristic* pRemoteCharacteristic;
static BLEAdvertisedDevice* myDevice;
static void notifyCallback(
BLERemoteCharacteristic* pBLERemoteCharacteristic,
uint8_t* pData,
size_t length,
bool isNotify) {
printf("Notify callback for characteristic %s of data length %d data: %s\n",
pBLERemoteCharacteristic->getUUID().toString().c_str(),
length,
(char*)pData);
}
/** None of these are required as they will be handled by the library with defaults. **
** Remove as you see fit for your needs */
class MyClientCallback : public BLEClientCallbacks {
void onConnect(BLEClient* pclient) {
}
/** onDisconnect now takes a reason parameter to indicate the reason for disconnection
void onDisconnect(BLEClient* pclient) { */
void onDisconnect(BLEClient* pclient, int reason) {
connected = false;
printf("onDisconnect");
}
/***************** New - Security handled here ********************
****** Note: these are the same return values as defaults ********/
void onPassKeyEntry(const NimBLEConnInfo& connInfo){
printf("Server Passkey Entry\n");
/** This should prompt the user to enter the passkey displayed
* on the peer device.
*/
NimBLEDevice::injectPassKey(connInfo, 123456);
};
void onConfirmPIN(const NimBLEConnInfo& connInfo, uint32_t pass_key){
printf("The passkey YES/NO number: %" PRIu32 "\n", pass_key);
/** Inject false if passkeys don't match. */
NimBLEDevice::injectConfirmPIN(connInfo, true);
};
/** Pairing process complete, we can check the results in connInfo */
void onAuthenticationComplete(const NimBLEConnInfo& connInfo){
if(!connInfo.isEncrypted()) {
printf("Encrypt connection failed - disconnecting\n");
/** Find the client with the connection handle provided in desc */
NimBLEDevice::getClientByID(connInfo.getConnHandle())->disconnect();
return;
}
}
/*******************************************************************/
};
bool connectToServer() {
printf("Forming a connection to %s\n", myDevice->getAddress().toString().c_str());
BLEClient* pClient = BLEDevice::createClient();
printf(" - Created client\n");
pClient->setClientCallbacks(new MyClientCallback());
// Connect to the remove BLE Server.
pClient->connect(myDevice); // if you pass BLEAdvertisedDevice instead of address, it will be recognized type of peer device address (public or private)
printf(" - Connected to server\n");
// Obtain a reference to the service we are after in the remote BLE server.
BLERemoteService* pRemoteService = pClient->getService(serviceUUID);
if (pRemoteService == nullptr) {
printf("Failed to find our service UUID: %s\n", serviceUUID.toString().c_str());
pClient->disconnect();
return false;
}
printf(" - Found our service\n");
// Obtain a reference to the characteristic in the service of the remote BLE server.
pRemoteCharacteristic = pRemoteService->getCharacteristic(charUUID);
if (pRemoteCharacteristic == nullptr) {
printf("Failed to find our characteristic UUID: %s\n", charUUID.toString().c_str());
pClient->disconnect();
return false;
}
printf(" - Found our characteristic\n");
// Read the value of the characteristic.
if(pRemoteCharacteristic->canRead()) {
std::string value = pRemoteCharacteristic->readValue();
printf("The characteristic value was: %s\n", value.c_str());
}
/** registerForNotify() has been removed and replaced with subscribe() / unsubscribe().
* Subscribe parameter defaults are: notifications=true, notifyCallback=nullptr, response=true.
* Unsubscribe parameter defaults are: response=true.
*/
if(pRemoteCharacteristic->canNotify()) {
//pRemoteCharacteristic->registerForNotify(notifyCallback);
pRemoteCharacteristic->subscribe(true, notifyCallback);
}
connected = true;
return true;
}
/**
* Scan for BLE servers and find the first one that advertises the service we are looking for.
*/
class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks {
/**
* Called for each advertising BLE server.
*/
/*** Only a reference to the advertised device is passed now
void onResult(BLEAdvertisedDevice advertisedDevice) { **/
void onResult(BLEAdvertisedDevice* advertisedDevice) {
printf("BLE Advertised Device found: %s\n", advertisedDevice->toString().c_str());
// We have found a device, let us now see if it contains the service we are looking for.
/********************************************************************************
if (advertisedDevice.haveServiceUUID() && advertisedDevice.isAdvertisingService(serviceUUID)) {
********************************************************************************/
if (advertisedDevice->haveServiceUUID() && advertisedDevice->isAdvertisingService(serviceUUID)) {
BLEDevice::getScan()->stop();
/*******************************************************************
myDevice = new BLEAdvertisedDevice(advertisedDevice);
*******************************************************************/
myDevice = advertisedDevice; /** Just save the reference now, no need to copy the object */
doConnect = true;
doScan = true;
} // Found our server
} // onResult
}; // MyAdvertisedDeviceCallbacks
// This is the Arduino main loop function.
void connectTask (void * parameter){
for(;;) {
// If the flag "doConnect" is true then we have scanned for and found the desired
// BLE Server with which we wish to connect. Now we connect to it. Once we are
// connected we set the connected flag to be true.
if (doConnect == true) {
if (connectToServer()) {
printf("We are now connected to the BLE Server.\n");
} else {
printf("We have failed to connect to the server; there is nothin more we will do.\n");
}
doConnect = false;
}
// If we are connected to a peer BLE Server, update the characteristic each time we are reached
// with the current time since boot.
if (connected) {
char buf[256];
snprintf(buf, 256, "Time since boot: %lu", (unsigned long)(esp_timer_get_time() / 1000000ULL));
printf("Setting new characteristic value to %s\n", buf);
// Set the characteristic's value to be the array of bytes that is actually a string.
/*** Note: write value now returns true if successful, false otherwise - try again or disconnect ***/
pRemoteCharacteristic->writeValue((uint8_t*)buf, strlen(buf), false);
}else if(doScan){
BLEDevice::getScan()->start(0); // this is just eample to start scan after disconnect, most likely there is better way to do it
}
vTaskDelay(1000/portTICK_PERIOD_MS); // Delay a second between loops.
}
vTaskDelete(NULL);
} // End of loop
void app_main(void) {
printf("Starting BLE Client application...\n");
BLEDevice::init("");
// Retrieve a Scanner and set the callback we want to use to be informed when we
// have detected a new device. Specify that we want active scanning and start the
// scan to run for 5 seconds.
BLEScan* pBLEScan = BLEDevice::getScan();
pBLEScan->setScanCallbacks(new MyAdvertisedDeviceCallbacks());
pBLEScan->setInterval(1349);
pBLEScan->setWindow(449);
pBLEScan->setActiveScan(true);
xTaskCreate(connectTask, "connectTask", 5000, NULL, 1, NULL);
pBLEScan->start(5 * 1000, false);
} // End of setup.

View File

@ -1,12 +0,0 @@
# Override some defaults so BT stack is enabled
# in this example
#
# BT config
#
CONFIG_BT_ENABLED=y
CONFIG_BTDM_CTRL_MODE_BLE_ONLY=y
CONFIG_BTDM_CTRL_MODE_BR_EDR_ONLY=n
CONFIG_BTDM_CTRL_MODE_BTDM=n
CONFIG_BT_BLUEDROID_ENABLED=n
CONFIG_BT_NIMBLE_ENABLED=y

View File

@ -1,7 +0,0 @@
# The following lines of boilerplate have to be in your project's
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
set(SUPPORTED_TARGETS esp32)
project(BLE_notify)

View File

@ -1,3 +0,0 @@
PROJECT_NAME := BLE_notify
include $(IDF_PATH)/make/project.mk

View File

@ -1,4 +0,0 @@
#
# "main" pseudo-component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)

View File

@ -1,164 +0,0 @@
/*
Video: https://www.youtube.com/watch?v=oCMOYS71NIU
Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleNotify.cpp
Ported to Arduino ESP32 by Evandro Copercini
updated by chegewara
Refactored back to IDF by H2zero
Create a BLE server that, once we receive a connection, will send periodic notifications.
The service advertises itself as: 4fafc201-1fb5-459e-8fcc-c5c9c331914b
And has a characteristic of: beb5483e-36e1-4688-b7f5-ea07361b26a8
The design of creating the BLE server is:
1. Create a BLE Server
2. Create a BLE Service
3. Create a BLE Characteristic on the Service
4. Create a BLE Descriptor on the characteristic
5. Start the service.
6. Start advertising.
A connect hander associated with the server starts a background task that performs notification
every couple of seconds.
*/
/** NimBLE differences highlighted in comment blocks **/
/*******original********
#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <BLE2902.h>
***********************/
#include <NimBLEDevice.h>
extern "C" {void app_main(void);}
BLEServer* pServer = NULL;
BLECharacteristic* pCharacteristic = NULL;
bool deviceConnected = false;
bool oldDeviceConnected = false;
uint32_t value = 0;
// See the following for generating UUIDs:
// https://www.uuidgenerator.net/
#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8"
/** None of these are required as they will be handled by the library with defaults. **
** Remove as you see fit for your needs */
class MyServerCallbacks: public BLEServerCallbacks {
void onConnect(BLEServer* pServer, BLEConnInfo& connInfo) {
deviceConnected = true;
};
void onDisconnect(BLEServer* pServer, BLEConnInfo& connInfo, int reason) {
deviceConnected = false;
}
/***************** New - Security handled here ********************
****** Note: these are the same return values as defaults ********/
uint32_t onPassKeyDisplay(){
printf("Server Passkey Display\n");
/** This should return a random 6 digit number for security
* or make your own static passkey as done here.
*/
return 123456;
};
void onConfirmPIN(const NimBLEConnInfo& connInfo, uint32_t pass_key){
printf("The passkey YES/NO number: %" PRIu32 "\n", pass_key);
/** Inject false if passkeys don't match. */
NimBLEDevice::injectConfirmPIN(connInfo, true);
};
void onAuthenticationComplete(const NimBLEConnInfo& connInfo){
/** Check that encryption was successful, if not we disconnect the client */
if(!connInfo.isEncrypted()) {
NimBLEDevice::getServer()->disconnect(connInfo.getConnHandle());
printf("Encrypt connection failed - disconnecting client\n");
return;
}
printf("Starting BLE work!");
};
/*******************************************************************/
};
void connectedTask (void * parameter){
for(;;) {
// notify changed value
if (deviceConnected) {
pCharacteristic->setValue((uint8_t*)&value, 4);
pCharacteristic->notify();
value++;
vTaskDelay(100/portTICK_PERIOD_MS); // bluetooth stack will go into congestion, if too many packets are sent
}
// disconnecting
if (!deviceConnected && oldDeviceConnected) {
vTaskDelay(500/portTICK_PERIOD_MS); // give the bluetooth stack the chance to get things ready
pServer->startAdvertising(); // restart advertising
printf("start advertising\n");
oldDeviceConnected = deviceConnected;
}
// connecting
if (deviceConnected && !oldDeviceConnected) {
// do stuff here on connecting
oldDeviceConnected = deviceConnected;
}
vTaskDelay(10/portTICK_PERIOD_MS); // Delay between loops to reset watchdog timer
}
vTaskDelete(NULL);
}
void app_main(void) {
// Create the BLE Device
BLEDevice::init("ESP32");
// Create the BLE Server
pServer = BLEDevice::createServer();
pServer->setCallbacks(new MyServerCallbacks());
// Create the BLE Service
BLEService *pService = pServer->createService(SERVICE_UUID);
// Create a BLE Characteristic
pCharacteristic = pService->createCharacteristic(
CHARACTERISTIC_UUID,
/******* Enum Type NIMBLE_PROPERTY now *******
BLECharacteristic::PROPERTY_READ |
BLECharacteristic::PROPERTY_WRITE |
BLECharacteristic::PROPERTY_NOTIFY |
BLECharacteristic::PROPERTY_INDICATE
);
**********************************************/
NIMBLE_PROPERTY::READ |
NIMBLE_PROPERTY::WRITE |
NIMBLE_PROPERTY::NOTIFY |
NIMBLE_PROPERTY::INDICATE
);
// Create a BLE Descriptor
/***************************************************
NOTE: DO NOT create a 2902 descriptor.
it will be created automatically if notifications
or indications are enabled on a characteristic.
pCharacteristic->addDescriptor(new BLE2902());
****************************************************/
// Start the service
pService->start();
// Start advertising
BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
pAdvertising->addServiceUUID(SERVICE_UUID);
pAdvertising->setScanResponse(false);
/** This method had been removed **
pAdvertising->setMinPreferred(0x0); // set value to 0x00 to not advertise this parameter
**/
xTaskCreate(connectedTask, "connectedTask", 5000, NULL, 1, NULL);
BLEDevice::startAdvertising();
printf("Waiting a client connection to notify...\n");
}

View File

@ -1,12 +0,0 @@
# Override some defaults so BT stack is enabled
# in this example
#
# BT config
#
CONFIG_BT_ENABLED=y
CONFIG_BTDM_CTRL_MODE_BLE_ONLY=y
CONFIG_BTDM_CTRL_MODE_BR_EDR_ONLY=n
CONFIG_BTDM_CTRL_MODE_BTDM=n
CONFIG_BT_BLUEDROID_ENABLED=n
CONFIG_BT_NIMBLE_ENABLED=y

View File

@ -1,4 +0,0 @@
#
# "main" pseudo-component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)

View File

@ -1,52 +0,0 @@
/*
Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleScan.cpp
Ported to Arduino ESP32 by Evandro Copercini
Refactored back to IDF by H2zero
*/
/** NimBLE differences highlighted in comment blocks **/
/*******original********
#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEScan.h>
#include <BLEAdvertisedDevice.h>
***********************/
#include <NimBLEDevice.h>
extern "C"{void app_main(void);}
int scanTime = 5 * 1000; // In milliseconds, 0 = scan forever
BLEScan* pBLEScan;
class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks {
void onResult(BLEAdvertisedDevice* advertisedDevice) {
printf("Advertised Device: %s \n", advertisedDevice->toString().c_str());
}
};
void scanTask (void * parameter){
for(;;) {
// put your main code here, to run repeatedly:
BLEScanResults foundDevices = pBLEScan->getResults(scanTime, false);
printf("Devices found: %d\n", foundDevices.getCount());
printf("Scan done!\n");
pBLEScan->clearResults(); // delete results fromBLEScan buffer to release memory
vTaskDelay(2000/portTICK_PERIOD_MS); // Delay a second between loops.
}
vTaskDelete(NULL);
}
void app_main(void) {
printf("Scanning...\n");
BLEDevice::init("");
pBLEScan = BLEDevice::getScan(); //create new scan
pBLEScan->setScanCallbacks(new MyAdvertisedDeviceCallbacks());
pBLEScan->setActiveScan(true); //active scan uses more power, but get results faster
pBLEScan->setInterval(100);
pBLEScan->setWindow(99); // less or equal setInterval value
xTaskCreate(scanTask, "scanTask", 5000, NULL, 1, NULL);
}

View File

@ -1,12 +0,0 @@
# Override some defaults so BT stack is enabled
# in this example
#
# BT config
#
CONFIG_BT_ENABLED=y
CONFIG_BTDM_CTRL_MODE_BLE_ONLY=y
CONFIG_BTDM_CTRL_MODE_BR_EDR_ONLY=n
CONFIG_BTDM_CTRL_MODE_BTDM=n
CONFIG_BT_BLUEDROID_ENABLED=n
CONFIG_BT_NIMBLE_ENABLED=y

View File

@ -1,7 +0,0 @@
# The following lines of boilerplate have to be in your project's
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
set(SUPPORTED_TARGETS esp32)
project(BLE_server)

View File

@ -1,4 +0,0 @@
#
# "main" pseudo-component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)

View File

@ -1,57 +0,0 @@
/*
Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleServer.cpp
Ported to Arduino ESP32 by Evandro Copercini
updates by chegewara
Refactored back to IDF by H2zero
*/
/** NimBLE differences highlighted in comment blocks **/
/*******original********
#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEServer.h>
***********************/
#include <NimBLEDevice.h>
extern "C"{void app_main(void);}
// See the following for generating UUIDs:
// https://www.uuidgenerator.net/
#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8"
void app_main(void) {
printf("Starting BLE work!\n");
BLEDevice::init("Long name works now");
BLEServer *pServer = BLEDevice::createServer();
BLEService *pService = pServer->createService(SERVICE_UUID);
BLECharacteristic *pCharacteristic = pService->createCharacteristic(
CHARACTERISTIC_UUID,
/***** Enum Type NIMBLE_PROPERTY now *****
BLECharacteristic::PROPERTY_READ |
BLECharacteristic::PROPERTY_WRITE
);
*****************************************/
NIMBLE_PROPERTY::READ |
NIMBLE_PROPERTY::WRITE
);
pCharacteristic->setValue("Hello World says Neil");
pService->start();
// BLEAdvertising *pAdvertising = pServer->getAdvertising(); // this still is working for backward compatibility
BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
pAdvertising->addServiceUUID(SERVICE_UUID);
pAdvertising->setScanResponse(true);
/** These methods have been removed **
pAdvertising->setMinPreferred(0x06); // functions that help with iPhone connections issue
pAdvertising->setMinPreferred(0x12);
*/
BLEDevice::startAdvertising();
printf("Characteristic defined! Now you can read it in your phone!\n");
}

View File

@ -1,12 +0,0 @@
# Override some defaults so BT stack is enabled
# in this example
#
# BT config
#
CONFIG_BT_ENABLED=y
CONFIG_BTDM_CTRL_MODE_BLE_ONLY=y
CONFIG_BTDM_CTRL_MODE_BR_EDR_ONLY=n
CONFIG_BTDM_CTRL_MODE_BTDM=n
CONFIG_BT_BLUEDROID_ENABLED=n
CONFIG_BT_NIMBLE_ENABLED=y

View File

@ -1,3 +0,0 @@
PROJECT_NAME := BLE_uart
include $(IDF_PATH)/make/project.mk

View File

@ -1,4 +0,0 @@
set(COMPONENT_SRCS "main.cpp")
set(COMPONENT_ADD_INCLUDEDIRS ".")
register_component()

View File

@ -1,4 +0,0 @@
#
# "main" pseudo-component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)

View File

@ -1,177 +0,0 @@
/*
Video: https://www.youtube.com/watch?v=oCMOYS71NIU
Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleNotify.cpp
Ported to Arduino ESP32 by Evandro Copercini
Refactored back to IDF by H2zero
Create a BLE server that, once we receive a connection, will send periodic notifications.
The service advertises itself as: 6E400001-B5A3-F393-E0A9-E50E24DCCA9E
Has a characteristic of: 6E400002-B5A3-F393-E0A9-E50E24DCCA9E - used for receiving data with "WRITE"
Has a characteristic of: 6E400003-B5A3-F393-E0A9-E50E24DCCA9E - used to send data with "NOTIFY"
The design of creating the BLE server is:
1. Create a BLE Server
2. Create a BLE Service
3. Create a BLE Characteristic on the Service
4. Create a BLE Descriptor on the characteristic
5. Start the service.
6. Start advertising.
In this example rxValue is the data received (only accessible inside that function).
And txValue is the data to be sent, in this example just a byte incremented every second.
*/
/** NimBLE differences highlighted in comment blocks **/
/*******original********
#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <BLE2902.h>
***********************/
#include <NimBLEDevice.h>
extern "C"{void app_main(void);}
BLEServer *pServer = NULL;
BLECharacteristic * pTxCharacteristic;
bool deviceConnected = false;
bool oldDeviceConnected = false;
uint8_t txValue = 0;
// See the following for generating UUIDs:
// https://www.uuidgenerator.net/
#define SERVICE_UUID "6E400001-B5A3-F393-E0A9-E50E24DCCA9E" // UART service UUID
#define CHARACTERISTIC_UUID_RX "6E400002-B5A3-F393-E0A9-E50E24DCCA9E"
#define CHARACTERISTIC_UUID_TX "6E400003-B5A3-F393-E0A9-E50E24DCCA9E"
/** None of these are required as they will be handled by the library with defaults. **
** Remove as you see fit for your needs */
class MyServerCallbacks: public BLEServerCallbacks {
void onConnect(BLEServer* pServer, BLEConnInfo& connInfo) {
deviceConnected = true;
};
void onDisconnect(BLEServer* pServer, BLEConnInfo& connInfo, int reason) {
deviceConnected = false;
}
/***************** New - Security handled here ********************
****** Note: these are the same return values as defaults ********/
uint32_t onPassKeyDisplay(){
printf("Server Passkey Display\n");
/** This should return a random 6 digit number for security
* or make your own static passkey as done here.
*/
return 123456;
};
void onConfirmPIN(const NimBLEConnInfo& connInfo, uint32_t pass_key){
printf("The passkey YES/NO number: %" PRIu32 "\n", pass_key);
/** Inject false if passkeys don't match. */
NimBLEDevice::injectConfirmPIN(connInfo, true);
};
void onAuthenticationComplete(const NimBLEConnInfo& connInfo){
/** Check that encryption was successful, if not we disconnect the client */
if(!connInfo.isEncrypted()) {
NimBLEDevice::getServer()->disconnect(connInfo.getConnHandle());
printf("Encrypt connection failed - disconnecting client\n");
return;
}
printf("Starting BLE work!");
};
/*******************************************************************/
};
class MyCallbacks: public BLECharacteristicCallbacks {
void onWrite(BLECharacteristic *pCharacteristic, BLEConnInfo& connInfo) {
std::string rxValue = pCharacteristic->getValue();
if (rxValue.length() > 0) {
printf("*********\n");
printf("Received Value: ");
for (int i = 0; i < rxValue.length(); i++)
printf("%d", rxValue[i]);
printf("\n*********\n");
}
}
};
void connectedTask (void * parameter){
for(;;) {
if (deviceConnected) {
pTxCharacteristic->setValue(&txValue, 1);
pTxCharacteristic->notify();
txValue++;
}
// disconnecting
if (!deviceConnected && oldDeviceConnected) {
pServer->startAdvertising(); // restart advertising
printf("start advertising\n");
oldDeviceConnected = deviceConnected;
}
// connecting
if (deviceConnected && !oldDeviceConnected) {
// do stuff here on connecting
oldDeviceConnected = deviceConnected;
}
vTaskDelay(10/portTICK_PERIOD_MS); // Delay between loops to reset watchdog timer
}
vTaskDelete(NULL);
}
void app_main(void) {
// Create the BLE Device
BLEDevice::init("UART Service");
// Create the BLE Server
pServer = BLEDevice::createServer();
pServer->setCallbacks(new MyServerCallbacks());
// Create the BLE Service
BLEService *pService = pServer->createService(SERVICE_UUID);
// Create a BLE Characteristic
pTxCharacteristic = pService->createCharacteristic(
CHARACTERISTIC_UUID_TX,
/******* Enum Type NIMBLE_PROPERTY now *******
BLECharacteristic::PROPERTY_NOTIFY
);
**********************************************/
NIMBLE_PROPERTY::NOTIFY
);
/***************************************************
NOTE: DO NOT create a 2902 descriptor
it will be created automatically if notifications
or indications are enabled on a characteristic.
pCharacteristic->addDescriptor(new BLE2902());
****************************************************/
BLECharacteristic * pRxCharacteristic = pService->createCharacteristic(
CHARACTERISTIC_UUID_RX,
/******* Enum Type NIMBLE_PROPERTY now *******
BLECharacteristic::PROPERTY_WRITE
);
*********************************************/
NIMBLE_PROPERTY::WRITE
);
pRxCharacteristic->setCallbacks(new MyCallbacks());
// Start the service
pService->start();
xTaskCreate(connectedTask, "connectedTask", 5000, NULL, 1, NULL);
// Start advertising
pServer->getAdvertising()->start();
printf("Waiting a client connection to notify...\n");
}

View File

@ -1,12 +0,0 @@
# Override some defaults so BT stack is enabled
# in this example
#
# BT config
#
CONFIG_BT_ENABLED=y
CONFIG_BTDM_CTRL_MODE_BLE_ONLY=y
CONFIG_BTDM_CTRL_MODE_BR_EDR_ONLY=n
CONFIG_BTDM_CTRL_MODE_BTDM=n
CONFIG_BT_BLUEDROID_ENABLED=n
CONFIG_BT_NIMBLE_ENABLED=y

View File

@ -0,0 +1,25 @@
## IDF Component Manager Manifest File
version: "2.3.0"
license: "Apache-2.0"
description: "C++ wrapper for the NimBLE BLE stack"
url: "https://github.com/h2zero/esp-nimble-cpp"
repository: "https://github.com/h2zero/esp-nimble-cpp"
maintainers:
- Ryan Powell <ryan@nable-embedded.io>
documentation: "https://h2zero.github.io/esp-nimble-cpp/"
tags:
- BLE
- NimBLE
dependencies:
espressif/esp_hosted:
version: "*"
rules:
- if: "target in [esp32p4]"
espressif/esp_wifi_remote:
version: ">=0.5.3"
rules:
- if: "target in [esp32p4]"
idf:
version: ">=5.3.0"
rules:
- if: "target in [esp32p4]"

View File

@ -1,8 +1,23 @@
{ {
"name": "esp-nimble-cpp", "name": "esp-nimble-cpp",
"keywords": "esp32, bluetooth", "version": "2.2.1",
"description": "Bluetooth low energy (BLE) library for esp32 based on NimBLE", "description": "C++ wrapper for the NimBLE BLE stack",
"version": "1.4.1", "keywords": [
"frameworks": "arduino", "BLE",
"platforms": "espressif32" "espidf",
"espressif",
"esp32",
"nimble"
],
"license": "Apache-2.0",
"repository": {
"type": "git",
"url": "https://github.com/h2zero/esp-nimble-cpp"
},
"authors": {
"name": "Ryan Powell",
"email": "ryan@nable-embedded.io",
"url": "https://github.com/h2zero/esp-nimble-cpp",
"maintainer": true
}
} }

View File

@ -1,10 +0,0 @@
name=esp-nimble-cpp
version=1.4.1
author=h2zero
maintainer=h2zero <powellperalta@gmail.com>
sentence=Bluetooth low energy (BLE) library for esp32 based on NimBLE.
paragraph=This is a more updated and lower resource alternative to the original bluedroid BLE library for esp32. Uses 50% less flash space and approximately 100KB less ram with the same functionality. Nearly 100% compatible with existing application code, migration guide included.
url=https://github.com/h2zero/esp-nimble-cpp
category=Communication
architectures=esp32,arm-ble
includes=NimBLEDevice.h

View File

@ -1,17 +0,0 @@
{
"name": "esp-nimble-cpp",
"version": "1.5.0",
"description": "NimBLE, BLE stack for the Espressif ESP32, ESP32-S and ESP32-C series of SoCs",
"keywords": [
"BLE",
"espidf",
"arduino",
"espressif",
"esp32"
],
"license": "LGPL-2.1-or-later",
"repository": {
"type": "git",
"url": "https://github.com/h2zero/esp-nimble-cpp"
}
}

View File

@ -25,9 +25,9 @@
#define HID_VERSION_1_11 (0x0111) #define HID_VERSION_1_11 (0x0111)
/* HID Class */ /* HID Class */
#define HID_CLASS (3) #define BLE_HID_CLASS (3)
#define HID_SUBCLASS_NONE (0) #define BLE_HID_SUBCLASS_NONE (0)
#define HID_PROTOCOL_NONE (0) #define BLE_HID_PROTOCOL_NONE (0)
/* Descriptors */ /* Descriptors */
#define HID_DESCRIPTOR (33) #define HID_DESCRIPTOR (33)

View File

@ -1,82 +1,72 @@
/* /*
* NimBLE2904.cpp * Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
* *
* Created: on March 13, 2020 * Licensed under the Apache License, Version 2.0 (the "License");
* Author H2zero * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* *
* Originally: * http://www.apache.org/licenses/LICENSE-2.0
* *
* BLE2904.cpp * Unless required by applicable law or agreed to in writing, software
* * distributed under the License is distributed on an "AS IS" BASIS,
* Created on: Dec 23, 2017 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* Author: kolban * See the License for the specific language governing permissions and
* limitations under the License.
*/ */
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#include "NimBLE2904.h" #include "NimBLE2904.h"
#if CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL
NimBLE2904::NimBLE2904(NimBLECharacteristic* pChr)
NimBLE2904::NimBLE2904(NimBLECharacteristic* pCharacteristic) : NimBLEDescriptor(NimBLEUUID((uint16_t)0x2904), BLE_GATT_CHR_F_READ, sizeof(NimBLE2904Data), pChr) {
: NimBLEDescriptor(NimBLEUUID((uint16_t) 0x2904), setValue(m_data);
BLE_GATT_CHR_F_READ, } // NimBLE2904
sizeof(BLE2904_Data),
pCharacteristic)
{
m_data.m_format = 0;
m_data.m_exponent = 0;
m_data.m_namespace = 1; // 1 = Bluetooth SIG Assigned Numbers
m_data.m_unit = 0;
m_data.m_description = 0;
setValue((uint8_t*) &m_data, sizeof(m_data));
} // BLE2904
/** /**
* @brief Set the description. * @brief Set the description.
* @param [in] description The description value to set.
*/ */
void NimBLE2904::setDescription(uint16_t description) { void NimBLE2904::setDescription(uint16_t description) {
m_data.m_description = description; m_data.m_description = description;
setValue((uint8_t*) &m_data, sizeof(m_data)); setValue(m_data);
} } // setDescription
/** /**
* @brief Set the exponent. * @brief Set the exponent.
* @param [in] exponent The exponent value to set.
*/ */
void NimBLE2904::setExponent(int8_t exponent) { void NimBLE2904::setExponent(int8_t exponent) {
m_data.m_exponent = exponent; m_data.m_exponent = exponent;
setValue((uint8_t*) &m_data, sizeof(m_data)); setValue(m_data);
} // setExponent } // setExponent
/** /**
* @brief Set the format. * @brief Set the format.
* @param [in] format The format value to set.
*/ */
void NimBLE2904::setFormat(uint8_t format) { void NimBLE2904::setFormat(uint8_t format) {
m_data.m_format = format; m_data.m_format = format;
setValue((uint8_t*) &m_data, sizeof(m_data)); setValue(m_data);
} // setFormat } // setFormat
/** /**
* @brief Set the namespace. * @brief Set the namespace.
* @param [in] namespace_value The namespace value toset.
*/ */
void NimBLE2904::setNamespace(uint8_t namespace_value) { void NimBLE2904::setNamespace(uint8_t namespace_value) {
m_data.m_namespace = namespace_value; m_data.m_namespace = namespace_value;
setValue((uint8_t*) &m_data, sizeof(m_data)); setValue(m_data);
} // setNamespace } // setNamespace
/** /**
* @brief Set the units for this value. It should be one of the encoded values defined here: * @brief Set the units for this value.
* https://www.bluetooth.com/specifications/assigned-numbers/units
* @param [in] unit The type of units of this characteristic as defined by assigned numbers. * @param [in] unit The type of units of this characteristic as defined by assigned numbers.
* @details See https://www.bluetooth.com/specifications/assigned-numbers/units
*/ */
void NimBLE2904::setUnit(uint16_t unit) { void NimBLE2904::setUnit(uint16_t unit) {
m_data.m_unit = unit; m_data.m_unit = unit;
setValue((uint8_t*) &m_data, sizeof(m_data)); setValue(m_data);
} // setUnit } // setUnit
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL */ #endif // CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL

View File

@ -1,42 +1,44 @@
/* /*
* NimBLE2904.h * Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
* *
* Created: on March 13, 2020 * Licensed under the Apache License, Version 2.0 (the "License");
* Author H2zero * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* *
* Originally: * http://www.apache.org/licenses/LICENSE-2.0
* *
* BLE2904.h * Unless required by applicable law or agreed to in writing, software
* * distributed under the License is distributed on an "AS IS" BASIS,
* Created on: Dec 23, 2017 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* Author: kolban * See the License for the specific language governing permissions and
* limitations under the License.
*/ */
#ifndef MAIN_NIMBLE2904_H_ #ifndef NIMBLE_CPP_2904_H_
#define MAIN_NIMBLE2904_H_ #define NIMBLE_CPP_2904_H_
#include "nimconfig.h" #include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) #if CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL
#include "NimBLEDescriptor.h" # include "NimBLEDescriptor.h"
struct BLE2904_Data {
uint8_t m_format;
int8_t m_exponent;
uint16_t m_unit; // See https://www.bluetooth.com/specifications/assigned-numbers/units
uint8_t m_namespace;
uint16_t m_description;
struct NimBLE2904Data {
uint8_t m_format{0};
int8_t m_exponent{0};
uint16_t m_unit{0x2700}; // Unitless; See https://www.bluetooth.com/specifications/assigned-numbers/units
uint8_t m_namespace{1}; // 1 = Bluetooth SIG Assigned Numbers
uint16_t m_description{0}; // unknown description
} __attribute__((packed)); } __attribute__((packed));
/** /**
* @brief Descriptor for Characteristic Presentation Format. * @brief Descriptor for Characteristic Presentation Format.
* *
* This is a convenience descriptor for the Characteristic Presentation Format which has a UUID of 0x2904. * This is a convenience descriptor for the Characteristic Presentation Format which has a UUID of 0x2904.
*/ */
class NimBLE2904: public NimBLEDescriptor { class NimBLE2904 : public NimBLEDescriptor {
public: public:
NimBLE2904(NimBLECharacteristic* pCharacterisitic = nullptr); NimBLE2904(NimBLECharacteristic* pChr = nullptr);
static const uint8_t FORMAT_BOOLEAN = 1; static const uint8_t FORMAT_BOOLEAN = 1;
static const uint8_t FORMAT_UINT2 = 2; static const uint8_t FORMAT_UINT2 = 2;
static const uint8_t FORMAT_UINT4 = 3; static const uint8_t FORMAT_UINT4 = 3;
@ -64,17 +66,18 @@ public:
static const uint8_t FORMAT_UTF8 = 25; static const uint8_t FORMAT_UTF8 = 25;
static const uint8_t FORMAT_UTF16 = 26; static const uint8_t FORMAT_UTF16 = 26;
static const uint8_t FORMAT_OPAQUE = 27; static const uint8_t FORMAT_OPAQUE = 27;
static const uint8_t FORMAT_MEDASN1 = 28;
void setDescription(uint16_t); void setDescription(uint16_t description);
void setExponent(int8_t exponent); void setExponent(int8_t exponent);
void setFormat(uint8_t format); void setFormat(uint8_t format);
void setNamespace(uint8_t namespace_value); void setNamespace(uint8_t namespace_value);
void setUnit(uint16_t unit); void setUnit(uint16_t unit);
private: private:
friend class NimBLECharacteristic; friend class NimBLECharacteristic;
BLE2904_Data m_data; NimBLE2904Data m_data{};
}; // BLE2904 }; // NimBLE2904
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL */ #endif // CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL
#endif /* MAIN_NIMBLE2904_H_ */ #endif // NIMBLE_CPP_2904_H_

View File

@ -1,161 +1,185 @@
/* /*
* NimBLEAddress.cpp * Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
* *
* Created: on Jan 24 2020 * Licensed under the Apache License, Version 2.0 (the "License");
* Author H2zero * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* *
* Originally: * http://www.apache.org/licenses/LICENSE-2.0
* *
* BLEAddress.cpp * Unless required by applicable law or agreed to in writing, software
* * distributed under the License is distributed on an "AS IS" BASIS,
* Created on: Jul 2, 2017 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* Author: kolban * See the License for the specific language governing permissions and
* limitations under the License.
*/ */
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include <algorithm>
#include "NimBLEAddress.h" #include "NimBLEAddress.h"
#include "NimBLEUtils.h" #if CONFIG_BT_ENABLED
#include "NimBLELog.h"
# include "NimBLELog.h"
# include <algorithm>
# ifdef CONFIG_NIMBLE_CPP_ADDR_FMT_EXCLUDE_DELIMITER
# define NIMBLE_CPP_ADDR_DELIMITER ""
# else
# define NIMBLE_CPP_ADDR_DELIMITER ":"
# endif
# ifdef CONFIG_NIMBLE_CPP_ADDR_FMT_UPPERCASE
# define NIMBLE_CPP_ADDR_FMT "%02X%s%02X%s%02X%s%02X%s%02X%s%02X"
# else
# define NIMBLE_CPP_ADDR_FMT "%02x%s%02x%s%02x%s%02x%s%02x%s%02x"
# endif
static const char* LOG_TAG = "NimBLEAddress"; static const char* LOG_TAG = "NimBLEAddress";
/************************************************* /*************************************************
* NOTE: NimBLE address bytes are in INVERSE ORDER! * NOTE: NimBLE address bytes are in INVERSE ORDER!
* We will accomodate that fact in these methods. * We will accommodate that fact in these methods.
*************************************************/ *************************************************/
/** /**
* @brief Create an address from the native NimBLE representation. * @brief Create an address from the native NimBLE representation.
* @param [in] address The native NimBLE address. * @param [in] address The native NimBLE address.
*/ */
NimBLEAddress::NimBLEAddress(ble_addr_t address) { NimBLEAddress::NimBLEAddress(ble_addr_t address) : ble_addr_t{address} {}
memcpy(m_address, address.val, 6);
m_addrType = address.type;
} // NimBLEAddress
/** /**
* @brief Create a blank address, i.e. 00:00:00:00:00:00, type 0. * @brief Create an address from a hex string.
*/
NimBLEAddress::NimBLEAddress() {
NimBLEAddress("");
} // NimBLEAddress
/**
* @brief Create an address from a hex string
* *
* A hex string is of the format: * A hex string is of the format:
* ``` * ```
* 00:00:00:00:00:00 * 00:00:00:00:00:00
* ``` * ```
* which is 17 characters in length. * which is 17 characters in length.
* * @param [in] addr The hex string representation of the address.
* @param [in] stringAddress The hex string representation of the address. * @param [in] type The type of the address, should be one of:
* @param [in] type The type of the address. * * BLE_ADDR_PUBLIC (0)
* * BLE_ADDR_RANDOM (1)
*/ */
NimBLEAddress::NimBLEAddress(const std::string &stringAddress, uint8_t type) { NimBLEAddress::NimBLEAddress(const std::string& addr, uint8_t type) {
m_addrType = type; this->type = type;
if (stringAddress.length() == 0) { if (addr.length() == BLE_DEV_ADDR_LEN) {
memset(m_address, 0, 6); std::reverse_copy(addr.data(), addr.data() + BLE_DEV_ADDR_LEN, this->val);
return; return;
} }
if (stringAddress.length() == 6) { if (addr.length() == 17) {
std::reverse_copy(stringAddress.data(), stringAddress.data() + 6, m_address); std::string mac{addr};
mac.erase(std::remove(mac.begin(), mac.end(), ':'), mac.end());
uint64_t address = std::stoull(mac, nullptr, 16);
memcpy(this->val, &address, sizeof(this->val));
return; return;
} }
if (stringAddress.length() != 17) { *this = NimBLEAddress{};
memset(m_address, 0, sizeof m_address); // "00:00:00:00:00:00" represents an invalid address NIMBLE_LOGE(LOG_TAG, "Invalid address '%s'", addr.c_str());
NIMBLE_LOGD(LOG_TAG, "Invalid address '%s'", stringAddress.c_str());
return;
}
int data[6];
if(sscanf(stringAddress.c_str(), "%x:%x:%x:%x:%x:%x", &data[5], &data[4], &data[3], &data[2], &data[1], &data[0]) != 6) {
memset(m_address, 0, sizeof m_address); // "00:00:00:00:00:00" represents an invalid address
NIMBLE_LOGD(LOG_TAG, "Invalid address '%s'", stringAddress.c_str());
}
for(size_t index = 0; index < sizeof m_address; index++) {
m_address[index] = data[index];
}
} // NimBLEAddress } // NimBLEAddress
/** /**
* @brief Constructor for compatibility with bluedroid esp library using native ESP representation. * @brief Constructor for compatibility with bluedroid esp library using native ESP representation.
* @param [in] address A uint8_t[6] or esp_bd_addr_t containing the address. * @param [in] address A uint8_t[6] or esp_bd_addr_t containing the address.
* @param [in] type The type of the address. * @param [in] type The type of the address should be one of:
* * BLE_ADDR_PUBLIC (0)
* * BLE_ADDR_RANDOM (1)
*/ */
NimBLEAddress::NimBLEAddress(uint8_t address[6], uint8_t type) { NimBLEAddress::NimBLEAddress(const uint8_t address[BLE_DEV_ADDR_LEN], uint8_t type) {
std::reverse_copy(address, address + sizeof m_address, m_address); std::reverse_copy(address, address + BLE_DEV_ADDR_LEN, this->val);
m_addrType = type; this->type = type;
} // NimBLEAddress } // NimBLEAddress
/** /**
* @brief Constructor for address using a hex value.\n * @brief Constructor for address using a hex value.\n
* Use the same byte order, so use 0xa4c1385def16 for "a4:c1:38:5d:ef:16" * Use the same byte order, so use 0xa4c1385def16 for "a4:c1:38:5d:ef:16"
* @param [in] address uint64_t containing the address. * @param [in] address uint64_t containing the address.
* @param [in] type The type of the address. * @param [in] type The type of the address should be one of:
* * BLE_ADDR_PUBLIC (0)
* * BLE_ADDR_RANDOM (1)
*/ */
NimBLEAddress::NimBLEAddress(const uint64_t &address, uint8_t type) { NimBLEAddress::NimBLEAddress(const uint64_t& address, uint8_t type) {
memcpy(m_address, &address, sizeof m_address); memcpy(this->val, &address, sizeof(this->val));
m_addrType = type; this->type = type;
} // NimBLEAddress } // NimBLEAddress
/** /**
* @brief Determine if this address equals another. * @brief Determine if this address equals another.
* @param [in] otherAddress The other address to compare against. * @param [in] otherAddress The other address to compare against.
* @return True if the addresses are equal. * @return True if the addresses are equal.
*/ */
bool NimBLEAddress::equals(const NimBLEAddress &otherAddress) const { bool NimBLEAddress::equals(const NimBLEAddress& otherAddress) const {
return *this == otherAddress; return *this == otherAddress;
} // equals } // equals
/** /**
* @brief Get the native representation of the address. * @brief Get the NimBLE base struct of the address.
* @return a pointer to the uint8_t[6] array of the address. * @return A read only reference to the NimBLE base struct of the address.
*/ */
const uint8_t *NimBLEAddress::getNative() const { const ble_addr_t* NimBLEAddress::getBase() const {
return m_address; return reinterpret_cast<const ble_addr_t*>(this);
} // getNative } // getBase
/** /**
* @brief Get the address type. * @brief Get the address type.
* @return The address type. * @return The address type.
*/ */
uint8_t NimBLEAddress::getType() const { uint8_t NimBLEAddress::getType() const {
return m_addrType; return this->type;
} // getType } // getType
/**
* @brief Get the address value.
* @return A read only reference to the address value.
*/
const uint8_t* NimBLEAddress::getVal() const {
return this->val;
} // getVal
/** /**
* @brief Determine if this address is a Resolvable Private Address. * @brief Determine if this address is a Resolvable Private Address.
* @return True if the address is a RPA. * @return True if the address is a RPA.
*/ */
bool NimBLEAddress::isRpa() const { bool NimBLEAddress::isRpa() const {
return (m_addrType && ((m_address[5] & 0xc0) == 0x40)); return BLE_ADDR_IS_RPA(this);
} // isRpa } // isRpa
/**
* @brief Determine if this address is a Non-Resolvable Private Address.
* @return True if the address is a NRPA.
*/
bool NimBLEAddress::isNrpa() const {
return BLE_ADDR_IS_NRPA(this);
} // isNrpa
/**
* @brief Determine if this address is a Static Address.
* @return True if the address is a Static Address.
*/
bool NimBLEAddress::isStatic() const {
return BLE_ADDR_IS_STATIC(this);
} // isStatic
/**
* @brief Determine if this address is a Public Address.
* @return True if the address is a Public Address.
*/
bool NimBLEAddress::isPublic() const {
return this->type == BLE_ADDR_PUBLIC;
} // isPublic
/**
* @brief Determine if this address is a NULL Address.
* @return True if the address is a NULL Address.
*/
bool NimBLEAddress::isNull() const {
return *this == NimBLEAddress{};
} // isNull
/** /**
* @brief Convert a BLE address to a string. * @brief Convert a BLE address to a string.
*
* A string representation of an address is in the format:
*
* ```
* xx:xx:xx:xx:xx:xx
* ```
*
* @return The string representation of the address. * @return The string representation of the address.
* @deprecated Use std::string() operator instead. * @deprecated Use std::string() operator instead.
*/ */
@ -163,43 +187,62 @@ std::string NimBLEAddress::toString() const {
return std::string(*this); return std::string(*this);
} // toString } // toString
/**
* @brief Reverse the byte order of the address.
* @return A reference to this address.
*/
const NimBLEAddress& NimBLEAddress::reverseByteOrder() {
std::reverse(this->val, this->val + BLE_DEV_ADDR_LEN);
return *this;
} // reverseByteOrder
/** /**
* @brief Convenience operator to check if this address is equal to another. * @brief Convenience operator to check if this address is equal to another.
*/ */
bool NimBLEAddress::operator ==(const NimBLEAddress & rhs) const { bool NimBLEAddress::operator==(const NimBLEAddress& rhs) const {
return memcmp(rhs.m_address, m_address, sizeof m_address) == 0; if (this->type != rhs.type) {
} // operator == return false;
}
return memcmp(rhs.val, this->val, sizeof(this->val)) == 0;
} // operator ==
/** /**
* @brief Convenience operator to check if this address is not equal to another. * @brief Convenience operator to check if this address is not equal to another.
*/ */
bool NimBLEAddress::operator !=(const NimBLEAddress & rhs) const { bool NimBLEAddress::operator!=(const NimBLEAddress& rhs) const {
return !this->operator==(rhs); return !this->operator==(rhs);
} // operator != } // operator !=
/** /**
* @brief Convienience operator to convert this address to string representation. * @brief Convenience operator to convert this address to string representation.
* @details This allows passing NimBLEAddress to functions * @details This allows passing NimBLEAddress to functions that accept std::string and/or it's methods as a parameter.
* that accept std::string and/or or it's methods as a parameter.
*/ */
NimBLEAddress::operator std::string() const { NimBLEAddress::operator std::string() const {
char buffer[18]; char buffer[18];
snprintf(buffer, sizeof(buffer), "%02x:%02x:%02x:%02x:%02x:%02x", snprintf(buffer,
m_address[5], m_address[4], m_address[3], sizeof(buffer),
m_address[2], m_address[1], m_address[0]); NIMBLE_CPP_ADDR_FMT,
return std::string(buffer); this->val[5],
NIMBLE_CPP_ADDR_DELIMITER,
this->val[4],
NIMBLE_CPP_ADDR_DELIMITER,
this->val[3],
NIMBLE_CPP_ADDR_DELIMITER,
this->val[2],
NIMBLE_CPP_ADDR_DELIMITER,
this->val[1],
NIMBLE_CPP_ADDR_DELIMITER,
this->val[0]);
return std::string{buffer};
} // operator std::string } // operator std::string
/** /**
* @brief Convenience operator to convert the native address representation to uint_64. * @brief Convenience operator to convert the native address representation to uint_64.
*/ */
NimBLEAddress::operator uint64_t() const { NimBLEAddress::operator uint64_t() const {
uint64_t address = 0; uint64_t address = 0;
memcpy(&address, m_address, sizeof m_address); memcpy(&address, this->val, sizeof(this->val));
return address; return address;
} // operator uint64_t } // operator uint64_t

View File

@ -1,63 +1,71 @@
/* /*
* NimBLEAddress.h * Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
* *
* Created: on Jan 24 2020 * Licensed under the Apache License, Version 2.0 (the "License");
* Author H2zero * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* *
* Originally: * http://www.apache.org/licenses/LICENSE-2.0
* *
* BLEAddress.h * Unless required by applicable law or agreed to in writing, software
* * distributed under the License is distributed on an "AS IS" BASIS,
* Created on: Jul 2, 2017 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* Author: kolban * See the License for the specific language governing permissions and
* limitations under the License.
*/ */
#ifndef COMPONENTS_NIMBLEADDRESS_H_ #ifndef NIMBLE_CPP_ADDRESS_H_
#define COMPONENTS_NIMBLEADDRESS_H_ #define NIMBLE_CPP_ADDRESS_H_
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED)
#if defined(CONFIG_NIMBLE_CPP_IDF) #include "nimconfig.h"
#include "nimble/ble.h" #if CONFIG_BT_ENABLED
#else
#include "nimble/nimble/include/nimble/ble.h" # if defined(CONFIG_NIMBLE_CPP_IDF)
#endif # include "nimble/ble.h"
# else
# include "nimble/nimble/include/nimble/ble.h"
# endif
/**** FIX COMPILATION ****/ /**** FIX COMPILATION ****/
#undef min # undef min
#undef max # undef max
/**************************/ /**************************/
#include <string> # include <string>
#include <algorithm>
/** /**
* @brief A %BLE device address. * @brief A BLE device address.
* *
* Every %BLE device has a unique address which can be used to identify it and form connections. * Every BLE device has a unique address which can be used to identify it and form connections.
*/ */
class NimBLEAddress { class NimBLEAddress : private ble_addr_t {
public: public:
NimBLEAddress(); /**
NimBLEAddress(ble_addr_t address); * @brief Create a blank address, i.e. 00:00:00:00:00:00, type 0.
NimBLEAddress(uint8_t address[6], uint8_t type = BLE_ADDR_PUBLIC); */
NimBLEAddress(const std::string &stringAddress, uint8_t type = BLE_ADDR_PUBLIC); NimBLEAddress() = default;
NimBLEAddress(const uint64_t &address, uint8_t type = BLE_ADDR_PUBLIC); NimBLEAddress(const ble_addr_t address);
bool isRpa() const; NimBLEAddress(const uint8_t address[BLE_DEV_ADDR_LEN], uint8_t type);
bool equals(const NimBLEAddress &otherAddress) const; NimBLEAddress(const std::string& stringAddress, uint8_t type);
const uint8_t* getNative() const; NimBLEAddress(const uint64_t& address, uint8_t type);
std::string toString() const;
uint8_t getType() const;
bool operator ==(const NimBLEAddress & rhs) const; bool isRpa() const;
bool operator !=(const NimBLEAddress & rhs) const; bool isNrpa() const;
operator std::string() const; bool isStatic() const;
operator uint64_t() const; bool isPublic() const;
bool isNull() const;
private: bool equals(const NimBLEAddress& otherAddress) const;
uint8_t m_address[6]; const ble_addr_t* getBase() const;
uint8_t m_addrType; std::string toString() const;
uint8_t getType() const;
const uint8_t* getVal() const;
const NimBLEAddress& reverseByteOrder();
bool operator==(const NimBLEAddress& rhs) const;
bool operator!=(const NimBLEAddress& rhs) const;
operator std::string() const;
operator uint64_t() const;
}; };
#endif /* CONFIG_BT_ENABLED */ #endif // CONFIG_BT_ENABLED
#endif /* COMPONENTS_NIMBLEADDRESS_H_ */ #endif // NIMBLE_CPP_ADDRESS_H_

View File

@ -1,36 +1,39 @@
/* /*
* NimBLEAdvertisedDevice.h * Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
* *
* Created: on Jan 24 2020 * Licensed under the Apache License, Version 2.0 (the "License");
* Author H2zero * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* *
* Originally: * http://www.apache.org/licenses/LICENSE-2.0
* *
* BLEAdvertisedDevice.h * Unless required by applicable law or agreed to in writing, software
* * distributed under the License is distributed on an "AS IS" BASIS,
* Created on: Jul 3, 2017 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* Author: kolban * See the License for the specific language governing permissions and
* limitations under the License.
*/ */
#ifndef COMPONENTS_NIMBLEADVERTISEDDEVICE_H_ #ifndef NIMBLE_CPP_ADVERTISED_DEVICE_H_
#define COMPONENTS_NIMBLEADVERTISEDDEVICE_H_ #define NIMBLE_CPP_ADVERTISED_DEVICE_H_
#include "nimconfig.h" #include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER) #if CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_OBSERVER
#include "NimBLEAddress.h" # include "NimBLEAddress.h"
#include "NimBLEScan.h" # include "NimBLEScan.h"
#include "NimBLEUUID.h" # include "NimBLEUUID.h"
#if defined(CONFIG_NIMBLE_CPP_IDF) # if defined(CONFIG_NIMBLE_CPP_IDF)
#include "host/ble_hs_adv.h" # include "host/ble_hs_adv.h"
#else # include "host/ble_gap.h"
#include "nimble/nimble/host/include/host/ble_hs_adv.h" # else
#endif # include "nimble/nimble/host/include/host/ble_hs_adv.h"
# include "nimble/nimble/host/include/host/ble_gap.h"
#include <map> # endif
#include <vector>
#include <time.h>
# include <vector>
class NimBLEScan; class NimBLEScan;
/** /**
@ -40,20 +43,61 @@ class NimBLEScan;
* class provides a model of a detected device. * class provides a model of a detected device.
*/ */
class NimBLEAdvertisedDevice { class NimBLEAdvertisedDevice {
public: public:
NimBLEAdvertisedDevice(); NimBLEAdvertisedDevice() = default;
NimBLEAddress getAddress(); uint8_t getAdvType() const;
uint8_t getAdvType(); uint8_t getAdvFlags() const;
uint8_t getAdvFlags(); uint16_t getAppearance() const;
uint16_t getAppearance(); uint16_t getAdvInterval() const;
uint16_t getAdvInterval(); uint16_t getMinInterval() const;
uint16_t getMinInterval(); uint16_t getMaxInterval() const;
uint16_t getMaxInterval(); uint8_t getManufacturerDataCount() const;
uint8_t getManufacturerDataCount(); const NimBLEAddress& getAddress() const;
std::string getManufacturerData(uint8_t index = 0); std::string getManufacturerData(uint8_t index = 0) const;
std::string getURI(); std::string getURI() const;
std::string getPayloadByType(uint16_t type); std::string getPayloadByType(uint16_t type, uint8_t index = 0) const;
std::string getName() const;
int8_t getRSSI() const;
NimBLEScan* getScan() const;
uint8_t getServiceDataCount() const;
std::string getServiceData(uint8_t index = 0) const;
std::string getServiceData(const NimBLEUUID& uuid) const;
NimBLEUUID getServiceDataUUID(uint8_t index = 0) const;
NimBLEUUID getServiceUUID(uint8_t index = 0) const;
uint8_t getServiceUUIDCount() const;
NimBLEAddress getTargetAddress(uint8_t index = 0) const;
uint8_t getTargetAddressCount() const;
int8_t getTXPower() const;
uint8_t getAdvLength() const;
uint8_t getAddressType() const;
bool isAdvertisingService(const NimBLEUUID& uuid) const;
bool haveAppearance() const;
bool haveManufacturerData() const;
bool haveName() const;
bool haveServiceData() const;
bool haveServiceUUID() const;
bool haveTXPower() const;
bool haveConnParams() const;
bool haveAdvInterval() const;
bool haveTargetAddress() const;
bool haveURI() const;
bool haveType(uint16_t type) const;
std::string toString() const;
bool isConnectable() const;
bool isScannable() const;
bool isLegacyAdvertisement() const;
# if CONFIG_BT_NIMBLE_EXT_ADV
uint8_t getSetId() const;
uint8_t getPrimaryPhy() const;
uint8_t getSecondaryPhy() const;
uint16_t getPeriodicInterval() const;
# endif
operator NimBLEAddress() const;
const std::vector<uint8_t>& getPayload() const;
const std::vector<uint8_t>::const_iterator begin() const;
const std::vector<uint8_t>::const_iterator end() const;
/** /**
* @brief A template to convert the service data to <type\>. * @brief A template to convert the service data to <type\>.
@ -63,21 +107,14 @@ public:
* less than <tt>sizeof(<type\>)</tt>. * less than <tt>sizeof(<type\>)</tt>.
* @details <b>Use:</b> <tt>getManufacturerData<type>(skipSizeCheck);</tt> * @details <b>Use:</b> <tt>getManufacturerData<type>(skipSizeCheck);</tt>
*/ */
template<typename T> template <typename T>
T getManufacturerData(bool skipSizeCheck = false) { T getManufacturerData(bool skipSizeCheck = false) const {
std::string data = getManufacturerData(); std::string data = getManufacturerData();
if(!skipSizeCheck && data.size() < sizeof(T)) return T(); if (!skipSizeCheck && data.size() < sizeof(T)) return T();
const char *pData = data.data(); const char* pData = data.data();
return *((T *)pData); return *((T*)pData);
} }
std::string getName();
int getRSSI();
NimBLEScan* getScan();
uint8_t getServiceDataCount();
std::string getServiceData(uint8_t index = 0);
std::string getServiceData(const NimBLEUUID &uuid);
/** /**
* @brief A template to convert the service data to <tt><type\></tt>. * @brief A template to convert the service data to <tt><type\></tt>.
* @tparam T The type to convert the data to. * @tparam T The type to convert the data to.
@ -87,12 +124,12 @@ public:
* less than <tt>sizeof(<type\>)</tt>. * less than <tt>sizeof(<type\>)</tt>.
* @details <b>Use:</b> <tt>getServiceData<type>(skipSizeCheck);</tt> * @details <b>Use:</b> <tt>getServiceData<type>(skipSizeCheck);</tt>
*/ */
template<typename T> template <typename T>
T getServiceData(uint8_t index = 0, bool skipSizeCheck = false) { T getServiceData(uint8_t index = 0, bool skipSizeCheck = false) const {
std::string data = getServiceData(index); std::string data = getServiceData(index);
if(!skipSizeCheck && data.size() < sizeof(T)) return T(); if (!skipSizeCheck && data.size() < sizeof(T)) return T();
const char *pData = data.data(); const char* pData = data.data();
return *((T *)pData); return *((T*)pData);
} }
/** /**
@ -104,80 +141,38 @@ public:
* less than <tt>sizeof(<type\>)</tt>. * less than <tt>sizeof(<type\>)</tt>.
* @details <b>Use:</b> <tt>getServiceData<type>(skipSizeCheck);</tt> * @details <b>Use:</b> <tt>getServiceData<type>(skipSizeCheck);</tt>
*/ */
template<typename T> template <typename T>
T getServiceData(const NimBLEUUID &uuid, bool skipSizeCheck = false) { T getServiceData(const NimBLEUUID& uuid, bool skipSizeCheck = false) const {
std::string data = getServiceData(uuid); std::string data = getServiceData(uuid);
if(!skipSizeCheck && data.size() < sizeof(T)) return T(); if (!skipSizeCheck && data.size() < sizeof(T)) return T();
const char *pData = data.data(); const char* pData = data.data();
return *((T *)pData); return *((T*)pData);
} }
NimBLEUUID getServiceDataUUID(uint8_t index = 0); private:
NimBLEUUID getServiceUUID(uint8_t index = 0);
uint8_t getServiceUUIDCount();
NimBLEAddress getTargetAddress(uint8_t index = 0);
uint8_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);
bool haveAppearance();
bool haveManufacturerData();
bool haveName();
bool haveRSSI();
bool haveServiceData();
bool haveServiceUUID();
bool haveTXPower();
bool haveConnParams();
bool haveAdvInterval();
bool haveTargetAddress();
bool haveURI();
bool haveType(uint16_t type);
std::string toString();
bool isConnectable();
bool isLegacyAdvertisement();
#if CONFIG_BT_NIMBLE_EXT_ADV
uint8_t getSetId();
uint8_t getPrimaryPhy();
uint8_t getSecondaryPhy();
uint16_t getPeriodicInterval();
#endif
private:
friend class NimBLEScan; friend class NimBLEScan;
void setAddress(NimBLEAddress address); NimBLEAdvertisedDevice(const ble_gap_event* event, uint8_t eventType);
void setAdvType(uint8_t advType, bool isLegacyAdv); void update(const ble_gap_event* event, uint8_t eventType);
void setPayload(const uint8_t *payload, uint8_t length, bool append); uint8_t findAdvField(uint8_t type, uint8_t index = 0, size_t* data_loc = nullptr) const;
void setRSSI(int rssi); size_t findServiceData(uint8_t index, uint8_t* bytes) const;
#if CONFIG_BT_NIMBLE_EXT_ADV
void setSetId(uint8_t sid) { m_sid = sid; }
void setPrimaryPhy(uint8_t phy) { m_primPhy = phy; }
void setSecondaryPhy(uint8_t phy) { m_secPhy = phy; }
void setPeriodicInterval(uint16_t itvl) { m_periodicItvl = itvl; }
#endif
uint8_t findAdvField(uint8_t type, uint8_t index = 0, size_t * data_loc = nullptr);
size_t findServiceData(uint8_t index, uint8_t* bytes);
NimBLEAddress m_address = NimBLEAddress(""); NimBLEAddress m_address{};
uint8_t m_advType; uint8_t m_advType{};
int m_rssi; int8_t m_rssi{};
time_t m_timestamp; uint8_t m_callbackSent{};
uint8_t m_callbackSent; uint8_t m_advLength{};
uint8_t m_advLength;
#if CONFIG_BT_NIMBLE_EXT_ADV
bool m_isLegacyAdv;
uint8_t m_sid;
uint8_t m_primPhy;
uint8_t m_secPhy;
uint16_t m_periodicItvl;
#endif
std::vector<uint8_t> m_payload; # if CONFIG_BT_NIMBLE_EXT_ADV
bool m_isLegacyAdv{};
uint8_t m_sid{};
uint8_t m_primPhy{};
uint8_t m_secPhy{};
uint16_t m_periodicItvl{};
# endif
std::vector<uint8_t> m_payload;
}; };
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_OBSERVER */ #endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_OBSERVER */
#endif /* COMPONENTS_NIMBLEADVERTISEDDEVICE_H_ */ #endif /* NIMBLE_CPP_ADVERTISED_DEVICE_H_ */

View File

@ -0,0 +1,586 @@
/*
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "NimBLEAdvertisementData.h"
#if (CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_BROADCASTER && !CONFIG_BT_NIMBLE_EXT_ADV) || defined(_DOXYGEN_)
# include "NimBLEDevice.h"
# include "NimBLEUtils.h"
# include "NimBLEUUID.h"
# include "NimBLELog.h"
# if defined(CONFIG_NIMBLE_CPP_IDF)
# include "host/ble_hs_adv.h"
# else
# include "nimble/nimble/host/include/host/ble_hs_adv.h"
# endif
static const char* LOG_TAG = "NimBLEAdvertisementData";
/**
* @brief Add data to the payload to be advertised.
* @param [in] data The data to be added to the payload.
* @param [in] length The size of data to be added to the payload.
*/
bool NimBLEAdvertisementData::addData(const uint8_t* data, size_t length) {
if (m_payload.size() + length > BLE_HS_ADV_MAX_SZ) {
NIMBLE_LOGE(LOG_TAG, "Data length exceeded");
return false;
}
m_payload.insert(m_payload.end(), data, data + length);
return true;
} // addData
/**
* @brief Add data to the payload to be advertised.
* @param [in] data The data to be added to the payload.
*/
bool NimBLEAdvertisementData::addData(const std::vector<uint8_t>& data) {
return addData(&data[0], data.size());
} // addData
/**
* @brief Set the appearance.
* @param [in] appearance The appearance code value.
* @return True if successful.
* @details If the appearance value is 0 then it will be removed from the advertisement if set previously.
*/
bool NimBLEAdvertisementData::setAppearance(uint16_t appearance) {
if (appearance == 0) {
return removeData(BLE_HS_ADV_TYPE_APPEARANCE);
}
uint8_t data[4];
data[0] = 3;
data[1] = BLE_HS_ADV_TYPE_APPEARANCE;
data[2] = appearance;
data[3] = (appearance >> 8) & 0xFF;
return addData(data, 4);
} // setAppearance
/**
* @brief Set the advertisement flags.
* @param [in] flag The flags to be set in the advertisement.
* * BLE_HS_ADV_F_DISC_LTD
* * BLE_HS_ADV_F_DISC_GEN
* * BLE_HS_ADV_F_BREDR_UNSUP - must always use with NimBLE
* A flag value of 0 will remove the flags from the advertisement.
*/
bool NimBLEAdvertisementData::setFlags(uint8_t flag) {
int dataLoc = getDataLocation(BLE_HS_ADV_TYPE_FLAGS);
if (dataLoc != -1) {
if (flag) {
m_payload[dataLoc + 2] = flag | BLE_HS_ADV_F_BREDR_UNSUP;
return true;
} else {
return removeData(BLE_HS_ADV_TYPE_FLAGS);
}
}
uint8_t data[3];
data[0] = 2;
data[1] = BLE_HS_ADV_TYPE_FLAGS;
data[2] = flag | BLE_HS_ADV_F_BREDR_UNSUP;
return addData(data, 3);
} // setFlags
/**
* @brief Adds Tx power level to the advertisement data.
* @return True if successful.
*/
bool NimBLEAdvertisementData::addTxPower() {
uint8_t data[3];
data[0] = BLE_HS_ADV_TX_PWR_LVL_LEN + 1;
data[1] = BLE_HS_ADV_TYPE_TX_PWR_LVL;
# ifndef CONFIG_IDF_TARGET_ESP32P4
data[2] = NimBLEDevice::getPower(NimBLETxPowerType::Advertise);
# else
data[2] = 0;
# endif
return addData(data, 3);
} // addTxPower
/**
* @brief Set the preferred min and max connection intervals to advertise.
* @param [in] minInterval The minimum preferred connection interval.
* @param [in] maxInterval The Maximum preferred connection interval.
* @details Range = 0x0006(7.5ms) to 0x0C80(4000ms), values not within the range will be limited to this range.
* @return True if successful.
*/
bool NimBLEAdvertisementData::setPreferredParams(uint16_t minInterval, uint16_t maxInterval) {
minInterval = std::max<uint16_t>(minInterval, 0x6);
minInterval = std::min<uint16_t>(minInterval, 0xC80);
maxInterval = std::max<uint16_t>(maxInterval, 0x6);
maxInterval = std::min<uint16_t>(maxInterval, 0xC80);
maxInterval = std::max<uint16_t>(maxInterval, minInterval); // Max must be greater than or equal to min.
uint8_t data[6];
data[0] = BLE_HS_ADV_SLAVE_ITVL_RANGE_LEN + 1;
data[1] = BLE_HS_ADV_TYPE_SLAVE_ITVL_RANGE;
data[2] = minInterval;
data[3] = minInterval >> 8;
data[4] = maxInterval;
data[5] = maxInterval >> 8;
return addData(data, 6);
} // setPreferredParams
/**
* @brief Add a service uuid to exposed list of services.
* @param [in] serviceUUID The UUID of the service to expose.
*/
bool NimBLEAdvertisementData::addServiceUUID(const NimBLEUUID& serviceUUID) {
uint8_t bytes = serviceUUID.bitSize() / 8;
int type;
switch (bytes) {
case 2:
type = BLE_HS_ADV_TYPE_COMP_UUIDS16;
break;
case 4:
type = BLE_HS_ADV_TYPE_COMP_UUIDS32;
break;
case 16:
type = BLE_HS_ADV_TYPE_COMP_UUIDS128;
break;
default:
NIMBLE_LOGE(LOG_TAG, "Cannot add UUID, invalid size!");
return false;
}
int dataLoc = getDataLocation(type);
uint8_t length = bytes;
if (dataLoc == -1) {
length += 2;
}
if (length + getPayload().size() > BLE_HS_ADV_MAX_SZ) {
NIMBLE_LOGE(LOG_TAG, "Cannot add UUID, data length exceeded!");
return false;
}
uint8_t data[BLE_HS_ADV_MAX_SZ];
const uint8_t* uuid = serviceUUID.getValue();
if (dataLoc == -1) {
data[0] = 1 + bytes;
data[1] = type;
memcpy(&data[2], uuid, bytes);
return addData(data, length);
}
m_payload.insert(m_payload.begin() + dataLoc + m_payload[dataLoc] + 1, uuid, uuid + bytes);
m_payload[dataLoc] += bytes;
return true;
} // addServiceUUID
/**
* @brief Add a service uuid to exposed list of services.
* @param [in] serviceUUID The string representation of the service to expose.
* @return True if successful.
*/
bool NimBLEAdvertisementData::addServiceUUID(const char* serviceUUID) {
return addServiceUUID(NimBLEUUID(serviceUUID));
} // addServiceUUID
/**
* @brief Remove a service UUID from the advertisement.
* @param [in] serviceUUID The UUID of the service to remove.
* @return True if successful or uuid not found, false if uuid error or data could not be reset.
*/
bool NimBLEAdvertisementData::removeServiceUUID(const NimBLEUUID& serviceUUID) {
uint8_t bytes = serviceUUID.bitSize() / 8;
int type;
switch (bytes) {
case 2:
type = BLE_HS_ADV_TYPE_COMP_UUIDS16;
break;
case 4:
type = BLE_HS_ADV_TYPE_COMP_UUIDS32;
break;
case 16:
type = BLE_HS_ADV_TYPE_COMP_UUIDS128;
break;
default:
NIMBLE_LOGE(LOG_TAG, "Cannot remove UUID, invalid size!");
return false;
}
int dataLoc = getDataLocation(type);
if (dataLoc == -1) {
return true;
}
int uuidLoc = -1;
for (size_t i = dataLoc + 2; i < m_payload.size(); i += bytes) {
if (memcmp(&m_payload[i], serviceUUID.getValue(), bytes) == 0) {
uuidLoc = i;
break;
}
}
if (uuidLoc == -1) {
return true;
}
if (m_payload[dataLoc] - bytes == 1) {
return removeData(type);
}
m_payload.erase(m_payload.begin() + uuidLoc, m_payload.begin() + uuidLoc + bytes);
m_payload[dataLoc] -= bytes;
return true;
} // removeServiceUUID
/**
* @brief Remove a service UUID from the advertisement.
* @param [in] serviceUUID The UUID of the service to remove.
* @return True if successful or uuid not found, false if uuid error or data could not be reset.
*/
bool NimBLEAdvertisementData::removeServiceUUID(const char* serviceUUID) {
return removeServiceUUID(NimBLEUUID(serviceUUID));
} // removeServiceUUID
/**
* @brief Remove all service UUIDs from the advertisement.
*/
bool NimBLEAdvertisementData::removeServices() {
return true;
} // removeServices
/**
* @brief Set manufacturer specific data.
* @param [in] data The manufacturer data to advertise.
* @param [in] length The length of the data.
* @return True if successful.
*/
bool NimBLEAdvertisementData::setManufacturerData(const uint8_t* data, size_t length) {
if (length > BLE_HS_ADV_MAX_FIELD_SZ) {
NIMBLE_LOGE(LOG_TAG, "MFG data too long");
return false;
}
uint8_t mdata[BLE_HS_ADV_MAX_SZ];
mdata[0] = length + 1;
mdata[1] = BLE_HS_ADV_TYPE_MFG_DATA;
memcpy(&mdata[2], data, length);
return addData(mdata, length + 2);
} // setManufacturerData
/**
* @brief Set manufacturer specific data.
* @param [in] data The manufacturer data to advertise.
* @return True if successful.
*/
bool NimBLEAdvertisementData::setManufacturerData(const std::string& data) {
return setManufacturerData(reinterpret_cast<const uint8_t*>(data.data()), data.length());
} // setManufacturerData
/**
* @brief Set manufacturer specific data.
* @param [in] data The manufacturer data to advertise.
* @return True if successful.
*/
bool NimBLEAdvertisementData::setManufacturerData(const std::vector<uint8_t>& data) {
return setManufacturerData(&data[0], data.size());
} // setManufacturerData
/**
* @brief Set the URI to advertise.
* @param [in] uri The uri to advertise.
* @return True if successful.
*/
bool NimBLEAdvertisementData::setURI(const std::string& uri) {
if (uri.length() > BLE_HS_ADV_MAX_FIELD_SZ) {
NIMBLE_LOGE(LOG_TAG, "URI too long");
return false;
}
uint8_t data[BLE_HS_ADV_MAX_SZ];
uint8_t length = 2 + uri.length();
data[0] = length - 1;
data[1] = BLE_HS_ADV_TYPE_URI;
memcpy(&data[2], uri.c_str(), uri.length());
return addData(data, length);
} // setURI
/**
* @brief Set the complete name of this device.
* @param [in] name The name to advertise.
* @param [in] isComplete If true the name is complete, which will set the data type accordingly.
* @details If the name is longer than 29 characters it will be truncated.
* and the data type will be set to incomplete name.
* @return True if successful.
*/
bool NimBLEAdvertisementData::setName(const std::string& name, bool isComplete) {
if (name.length() > BLE_HS_ADV_MAX_FIELD_SZ) {
NIMBLE_LOGE(LOG_TAG, "Name too long - truncating");
isComplete = false;
}
uint8_t data[BLE_HS_ADV_MAX_SZ];
uint8_t length = 2 + std::min<uint8_t>(name.length(), BLE_HS_ADV_MAX_FIELD_SZ);
data[0] = length - 1;
data[1] = isComplete ? BLE_HS_ADV_TYPE_COMP_NAME : BLE_HS_ADV_TYPE_INCOMP_NAME;
memcpy(&data[2], name.c_str(), std::min<uint8_t>(name.length(), BLE_HS_ADV_MAX_FIELD_SZ));
return addData(data, length);
} // setName
/**
* @brief Set the short name.
* @param [in] name The short name of the device.
* @return True if successful.
*/
bool NimBLEAdvertisementData::setShortName(const std::string& name) {
return setName(name, false);
} // setShortName
/**
* @brief Set a single service to advertise as a complete list of services.
* @param [in] uuid The service to advertise.
* @return True if successful.
*/
bool NimBLEAdvertisementData::setCompleteServices(const NimBLEUUID& uuid) {
return setServices(true, uuid.bitSize(), {uuid});
} // setCompleteServices
/**
* @brief Set the complete list of 16 bit services to advertise.
* @param [in] uuids A vector of 16 bit UUID's to advertise.
* @return True if successful.
*/
bool NimBLEAdvertisementData::setCompleteServices16(const std::vector<NimBLEUUID>& uuids) {
return setServices(true, 16, uuids);
} // setCompleteServices16
/**
* @brief Set the complete list of 32 bit services to advertise.
* @param [in] uuids A vector of 32 bit UUID's to advertise.
* @return True if successful.
*/
bool NimBLEAdvertisementData::setCompleteServices32(const std::vector<NimBLEUUID>& uuids) {
return setServices(true, 32, uuids);
} // setCompleteServices32
/**
* @brief Set a single service to advertise as a partial list of services.
* @param [in] uuid The service to advertise.
* @return True if successful.
*/
bool NimBLEAdvertisementData::setPartialServices(const NimBLEUUID& uuid) {
return setServices(false, uuid.bitSize(), {uuid});
} // setPartialServices
/**
* @brief Set the partial list of services to advertise.
* @param [in] uuids A vector of 16 bit UUID's to advertise.
* @return True if successful.
*/
bool NimBLEAdvertisementData::setPartialServices16(const std::vector<NimBLEUUID>& uuids) {
return setServices(false, 16, uuids);
} // setPartialServices16
/**
* @brief Set the partial list of services to advertise.
* @param [in] uuids A vector of 32 bit UUID's to advertise.
* @return True if successful.
*/
bool NimBLEAdvertisementData::setPartialServices32(const std::vector<NimBLEUUID>& uuids) {
return setServices(false, 32, uuids);
} // 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] uuids The vector of service UUID's to advertise.
* @return True if successful.
* @details The number of services will be truncated if the total length exceeds 31 bytes.
*/
bool NimBLEAdvertisementData::setServices(bool complete, uint8_t size, const std::vector<NimBLEUUID>& uuids) {
uint8_t bytes = size / 8;
uint8_t length = 2; // start with 2 for length + type bytes
uint8_t data[BLE_HS_ADV_MAX_SZ];
for (const auto& uuid : uuids) {
if (uuid.bitSize() != size) {
NIMBLE_LOGE(LOG_TAG, "Service UUID(%d) invalid", size);
continue;
} else {
if (length + bytes >= BLE_HS_ADV_MAX_SZ) {
NIMBLE_LOGW(LOG_TAG, "Too many services - truncating");
complete = false;
break;
}
memcpy(&data[length], uuid.getValue(), bytes);
length += bytes;
}
}
data[0] = length - 1; // don't count the length byte as part of the AD length
switch (size) {
case 16:
data[1] = (complete ? BLE_HS_ADV_TYPE_COMP_UUIDS16 : BLE_HS_ADV_TYPE_INCOMP_UUIDS16);
break;
case 32:
data[1] = (complete ? BLE_HS_ADV_TYPE_COMP_UUIDS32 : BLE_HS_ADV_TYPE_INCOMP_UUIDS32);
break;
case 128:
data[1] = (complete ? BLE_HS_ADV_TYPE_COMP_UUIDS128 : BLE_HS_ADV_TYPE_INCOMP_UUIDS128);
break;
default:
NIMBLE_LOGE(LOG_TAG, "Cannot set services, invalid size!");
return false;
}
return addData(data, length);
} // setServices
/**
* @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.
* @param [in] length The length of the data.
* @note If data length is 0 the service data will not be advertised.
* @return True if successful, false if data length is too long or could not be set.
*/
bool NimBLEAdvertisementData::setServiceData(const NimBLEUUID& uuid, const uint8_t* data, size_t length) {
uint8_t uuidBytes = uuid.bitSize() / 8;
uint8_t sDataLen = 2 + uuidBytes + length;
if (sDataLen > BLE_HS_ADV_MAX_SZ) {
NIMBLE_LOGE(LOG_TAG, "Service Data too long");
return false;
}
uint8_t type;
switch (uuidBytes) {
case 2:
type = BLE_HS_ADV_TYPE_SVC_DATA_UUID16;
break;
case 4:
type = BLE_HS_ADV_TYPE_SVC_DATA_UUID32;
break;
case 16:
type = BLE_HS_ADV_TYPE_SVC_DATA_UUID128;
break;
default:
NIMBLE_LOGE(LOG_TAG, "Cannot set service data, invalid size!");
return false;
}
if (length == 0) {
removeData(type);
return true;
}
uint8_t sData[BLE_HS_ADV_MAX_SZ];
sData[0] = uuidBytes + length + 1;
sData[1] = type;
memcpy(&sData[2], uuid.getValue(), uuidBytes);
memcpy(&sData[2 + uuidBytes], data, length);
return addData(sData, sDataLen);
} // setServiceData
/**
* @brief Set the service data (UUID + data)
* @param [in] uuid The UUID to set with the service data.
* @param [in] data The data to be associated with the service data advertised.
* @return True if the service data was set successfully.
* @note If data length is 0 the service data will not be advertised.
*/
bool NimBLEAdvertisementData::setServiceData(const NimBLEUUID& uuid, const std::string& data) {
return setServiceData(uuid, reinterpret_cast<const uint8_t*>(data.data()), data.length());
} // setServiceData
/**
* @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.
* @return True if the service data was set successfully.
* @note If data length is 0 the service data will not be advertised.
*/
bool NimBLEAdvertisementData::setServiceData(const NimBLEUUID& uuid, const std::vector<uint8_t>& data) {
return setServiceData(uuid, &data[0], data.size());
} // setServiceData
/**
* @brief Get the location of the data in the payload.
* @param [in] type The type of data to search for.
* @return -1 if the data is not found, otherwise the index of the data in the payload.
*/
int NimBLEAdvertisementData::getDataLocation(uint8_t type) const {
size_t index = 0;
while (index < m_payload.size()) {
if (m_payload[index + 1] == type) {
return index;
}
index += m_payload[index] + 1;
}
return -1;
} // getDataLocation
/**
* @brief Remove data from the advertisement data.
* @param [in] type The type of data to remove.
* @return True if successful, false if the data was not found.
*/
bool NimBLEAdvertisementData::removeData(uint8_t type) {
int dataLoc = getDataLocation(type);
if (dataLoc != -1) {
std::vector<uint8_t> swap(m_payload.begin(), m_payload.begin() + dataLoc);
int nextData = dataLoc + m_payload[dataLoc] + 1;
swap.insert(swap.end(), m_payload.begin() + nextData, m_payload.end());
swap.swap(m_payload);
return true;
}
return false;
} // removeData
/**
* @brief Retrieve the payload that is to be advertised.
* @return The payload of the advertisement data.
*/
std::vector<uint8_t> NimBLEAdvertisementData::getPayload() const {
return m_payload;
} // getPayload
/**
* @brief Clear the advertisement data for reuse.
*/
void NimBLEAdvertisementData::clearData() {
std::vector<uint8_t>().swap(m_payload);
} // clearData
/**
* @brief Get the string representation of the advertisement data.
* @return The string representation of the advertisement data.
*/
std::string NimBLEAdvertisementData::toString() const {
std::string hexStr = NimBLEUtils::dataToHexString(&m_payload[0], m_payload.size());
std::string str;
for (size_t i = 0; i < hexStr.length(); i += 2) {
str += hexStr[i];
str += hexStr[i + 1];
if (i + 2 < hexStr.length()) {
str += " ";
}
}
return str;
} // toString
#endif // (CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_BROADCASTER && !CONFIG_BT_NIMBLE_EXT_ADV) || defined(_DOXYGEN_)

View File

@ -0,0 +1,78 @@
/*
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef NIMBLE_CPP_ADVERTISEMENT_DATA_H_
#define NIMBLE_CPP_ADVERTISEMENT_DATA_H_
#include "nimconfig.h"
#if (CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_BROADCASTER && !CONFIG_BT_NIMBLE_EXT_ADV) || defined(_DOXYGEN_)
# include <cstdint>
# include <string>
# include <vector>
class NimBLEUUID;
/**
* @brief Advertisement data set by the programmer to be published by the BLE server.
*/
class NimBLEAdvertisementData {
// Only a subset of the possible BLE architected advertisement fields are currently exposed. Others will
// be exposed on demand/request or as time permits.
//
public:
bool addData(const uint8_t* data, size_t length);
bool addData(const std::vector<uint8_t>& data);
bool setAppearance(uint16_t appearance);
bool setFlags(uint8_t flag);
bool addTxPower();
bool setPreferredParams(uint16_t minInterval, uint16_t maxInterval);
bool addServiceUUID(const NimBLEUUID& serviceUUID);
bool addServiceUUID(const char* serviceUUID);
bool removeServiceUUID(const NimBLEUUID& serviceUUID);
bool removeServiceUUID(const char* serviceUUID);
bool removeServices();
bool setManufacturerData(const uint8_t* data, size_t length);
bool setManufacturerData(const std::string& data);
bool setManufacturerData(const std::vector<uint8_t>& data);
bool setURI(const std::string& uri);
bool setName(const std::string& name, bool isComplete = true);
bool setShortName(const std::string& name);
bool setCompleteServices(const NimBLEUUID& uuid);
bool setCompleteServices16(const std::vector<NimBLEUUID>& uuids);
bool setCompleteServices32(const std::vector<NimBLEUUID>& uuids);
bool setPartialServices(const NimBLEUUID& uuid);
bool setPartialServices16(const std::vector<NimBLEUUID>& uuids);
bool setPartialServices32(const std::vector<NimBLEUUID>& uuids);
bool setServiceData(const NimBLEUUID& uuid, const uint8_t* data, size_t length);
bool setServiceData(const NimBLEUUID& uuid, const std::string& data);
bool setServiceData(const NimBLEUUID& uuid, const std::vector<uint8_t>& data);
bool removeData(uint8_t type);
void clearData();
int getDataLocation(uint8_t type) const;
std::string toString() const;
std::vector<uint8_t> getPayload() const;
private:
friend class NimBLEAdvertising;
bool setServices(bool complete, uint8_t size, const std::vector<NimBLEUUID>& v_uuid);
std::vector<uint8_t> m_payload{};
}; // NimBLEAdvertisementData
#endif // (CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_BROADCASTER && !CONFIG_BT_NIMBLE_EXT_ADV) || defined(_DOXYGEN_)
#endif // NIMBLE_CPP_ADVERTISEMENT_DATA_H_

Some files were not shown because too many files have changed in this diff Show More