diff --git a/lib/libesp32/berry_tasmota/src/be_MI32_lib.c b/lib/libesp32/berry_tasmota/src/be_MI32_lib.c index 3babcf3c4..d3f4558f0 100644 --- a/lib/libesp32/berry_tasmota/src/be_MI32_lib.c +++ b/lib/libesp32/berry_tasmota/src/be_MI32_lib.c @@ -1,6 +1,6 @@ /******************************************************************** * Tasmota lib - * + * * To use: `import MI32` *******************************************************************/ #include "be_constobj.h" @@ -44,7 +44,7 @@ module MI32 (scope: global) { /******************************************************************** * Tasmota lib - * + * * 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); 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); 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); 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" @@ -93,11 +93,11 @@ module BLE (scope: global) { conn_cb, ctype_func(be_BLE_reg_conn_cb) set_svc, ctype_func(be_BLE_set_service) run, ctype_func(be_BLE_run) + store, ctype_func(be_BLE_store) set_chr, ctype_func(be_BLE_set_characteristic) adv_cb, ctype_func(be_BLE_reg_adv_cb) set_MAC, ctype_func(be_BLE_set_MAC) 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) } @const_object_info_end */ diff --git a/lib/libesp32_div/esp-nimble-cpp/.clang-format b/lib/libesp32_div/esp-nimble-cpp/.clang-format new file mode 100644 index 000000000..57a0bea89 --- /dev/null +++ b/lib/libesp32_div/esp-nimble-cpp/.clang-format @@ -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 diff --git a/lib/libesp32_div/esp-nimble-cpp/.github/workflows/build.yml b/lib/libesp32_div/esp-nimble-cpp/.github/workflows/build.yml index f256daaff..3664625d4 100644 --- a/lib/libesp32_div/esp-nimble-cpp/.github/workflows/build.yml +++ b/lib/libesp32_div/esp-nimble-cpp/.github/workflows/build.yml @@ -1,9 +1,11 @@ name: Build on: - workflow_dispatch: # Start a workflow + workflow_dispatch: pull_request: push: + branches: + - master jobs: build-esp-idf-component: @@ -15,35 +17,35 @@ jobs: # 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 # for details. - idf_ver: ["release-v4.4", "release-v5.1"] - idf_target: ["esp32", "esp32s3", "esp32c2", "esp32c3", "esp32c6"] + idf_ver: ["release-v4.4", "release-v5.4"] + idf_target: ["esp32", "esp32s3", "esp32c2", "esp32c3", "esp32c5", "esp32c6", "esp32h2", "esp32p4"] example: - - Advanced/NimBLE_Client - - Advanced/NimBLE_Server - - basic/BLE_client - - basic/BLE_notify - - basic/BLE_scan - - basic/BLE_server - - basic/BLE_uart + - NimBLE_Client + - NimBLE_Server - Bluetooth_5/NimBLE_extended_client - Bluetooth_5/NimBLE_extended_server - - Bluetooth_5/NimBLE_multi_advertiser exclude: - idf_target: "esp32" example: Bluetooth_5/NimBLE_extended_client - idf_target: "esp32" example: Bluetooth_5/NimBLE_extended_server - - idf_target: "esp32" - example: Bluetooth_5/NimBLE_multi_advertiser - idf_ver: release-v4.4 idf_target: "esp32c2" + - idf_ver: release-v4.4 + idf_target: "esp32c5" - idf_ver: release-v4.4 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 }} steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: path: components/esp-nimble-cpp - name: Build examples @@ -58,8 +60,8 @@ jobs: build_docs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Doxygen Action - uses: mattnotmitt/doxygen-action@v1.9.5 + uses: mattnotmitt/doxygen-action@v1.9.8 with: working-directory: 'docs/' diff --git a/lib/libesp32_div/esp-nimble-cpp/.github/workflows/release.yml b/lib/libesp32_div/esp-nimble-cpp/.github/workflows/release.yml new file mode 100644 index 000000000..ff66f7f8f --- /dev/null +++ b/lib/libesp32_div/esp-nimble-cpp/.github/workflows/release.yml @@ -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 \ No newline at end of file diff --git a/lib/libesp32_div/esp-nimble-cpp/.github/workflows/sponsors.yml b/lib/libesp32_div/esp-nimble-cpp/.github/workflows/sponsors.yml new file mode 100644 index 000000000..a7d4a53a0 --- /dev/null +++ b/lib/libesp32_div/esp-nimble-cpp/.github/workflows/sponsors.yml @@ -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' \ No newline at end of file diff --git a/lib/libesp32_div/esp-nimble-cpp/CHANGELOG.md b/lib/libesp32_div/esp-nimble-cpp/CHANGELOG.md index f8b6e42cb..492bb98eb 100644 --- a/lib/libesp32_div/esp-nimble-cpp/CHANGELOG.md +++ b/lib/libesp32_div/esp-nimble-cpp/CHANGELOG.md @@ -1,14 +1,336 @@ # Changelog - 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 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` or `uint8_t* + length` instead of `std::string` or `char + length`. +- `NimBLEAdvertisementData::getPayload` now returns `std::vector` 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` 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&` instead of non-const `std::vector*`. +- `NimBLERemoteService::getValue` now returns `NimBLEAttValue` instead of `std::string`. +- `NimBLEService::getCharacteristics` now returns a `const std::vector&` instead of std::vector. +- `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 -- 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 +- (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. +- `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& 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&` 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. +- `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 @@ -71,7 +393,7 @@ All notable changes to this project will be documented in this file. ## [1.3.0] - 2021-08-02 ### Added -- `NimBLECharacteristic::removeDescriptor`: Dynamically remove a descriptor from a characterisic. Takes effect after all connections are closed and sends a service changed indication. +- `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 - `NimBLEServerCallbacks::onMTUChange`: This is callback is called when the MTU is updated after connection with a client. - ESP32C3 support @@ -102,12 +424,12 @@ All notable changes to this project will be documented in this file. ### Fixed - `NimBLECharacteristicCallbacks::onSubscribe` Is now called after the connection is added to the vector. - Corrected bonding failure when reinitializing the BLE stack. -- Writing to a characterisic with a std::string value now correctly writes values with null characters. -- Retrieving remote descriptors now uses the characterisic end handle correctly. +- Writing to a characteristic with a std::string value now correctly writes values with null characters. +- Retrieving remote descriptors now uses the characteristic end handle correctly. - Missing data in long writes to remote descriptors. - Hanging on task notification when sending an indication from the characteristic callback. - BLE controller memory could be released when using Arduino as a component. -- Complile errors with NimBLE release 1.3.0. +- Compile errors with NimBLE release 1.3.0. ## [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::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) - `NimBLEService::getCharacteristics(const char *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` 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. -Instead the data will be parsed on-demand when the user application asks for specific data. +- `NimBLEAdvertisedDevice` Has been refactored to store the complete advertisement payload and no longer parses the data from each advertisement. +Instead the data will be parsed on-demand when the user application asks for specific data. ### Fixed - `NimBLEHIDDevice` Characteristics now use encryption, this resolves an issue with communicating with devices requiring encryption for HID devices. @@ -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 ### 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 -the NimBLERemoteCharacteristic object. +- `NimBLEClient::getCharacteristic(uint16_t handle)` Enabling the use of the characteristic handle to be used to find +the NimBLERemoteCharacteristic object. -- `NimBLEHIDDevice` class added by wakwak-koba. +- `NimBLEHIDDevice` class added by wakwak-koba. -- `NimBLEServerCallbacks::onDisconnect` overloaded callback added to provide a ble_gap_conn_desc parameter for the application -to obtain information about the disconnected client. +- `NimBLEServerCallbacks::onDisconnect` overloaded callback added to provide a ble_gap_conn_desc parameter for the application +to obtain information about the disconnected client. -- Conditional checks in `nimconfig.h` for command line defined macros to support platformio config settings. +- Conditional checks in `nimconfig.h` for command line defined macros to support platformio config settings. ### Changed -- `NimBLEAdvertising::start` now returns a bool value to indicate success/failure. +- `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 -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) -this allows the starting of a new scan from the callback function. +- `NimBLEScan` When the scan ends the scan stopped flag is now set before calling the scan complete callback (if used) +this allows the starting of a new scan from the callback function. ### Fixed -- Sometimes `NimBLEClient::connect` would hang on the task block if no event arrived to unblock. -A time limit has been added to timeout appropriately. +- Sometimes `NimBLEClient::connect` would hang on the task block if no event arrived to unblock. +A time limit has been added to timeout appropriately. -- When getting descriptors for a characterisic the end handle of the service was used as a proxy for the characteristic end -handle. This would be rejected by some devices and has been changed to use the next characteristic handle as the end when possible. +- 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. -- An exception could occur when deleting a client instance if a notification arrived while the attribute vectors were being -deleted. A flag has been added to prevent this. - -- An exception could occur after a host reset event when the host re-synced if the tasks that were stopped during the event did -not finish processing. A yield has been added after re-syncing to allow tasks to finish before proceeding. - -- Occasionally the controller would fail to send a disconnected event causing the client to indicate it is connected -and would be unable to reconnect. A timer has been added to reset the host/controller if it expires. - -- Occasionally the call to start scanning would get stuck in a loop on BLE_HS_EBUSY, this loop has been removed. +- 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. -- 16bit and 32bit UUID's in some cases were not discovered or compared correctly if the device -advertised them as 16/32bit but resolved them to 128bits. Both are now checked. - -- `FreeRTOS` compile errors resolved in latest Ardruino core and IDF v3.3. +- 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. -- 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 ### Changed -- `NimBLEAdvertising::start` Now takes 2 optional parameters, the first is the duration to advertise for (in seconds), the second is a -callback that is invoked when advertsing ends and takes a pointer to a `NimBLEAdvertising` object (similar to the `NimBLEScan::start` API). +- `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 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`. 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. diff --git a/lib/libesp32_div/esp-nimble-cpp/CMakeLists.txt b/lib/libesp32_div/esp-nimble-cpp/CMakeLists.txt index 4bed65b11..118158ed2 100644 --- a/lib/libesp32_div/esp-nimble-cpp/CMakeLists.txt +++ b/lib/libesp32_div/esp-nimble-cpp/CMakeLists.txt @@ -2,23 +2,28 @@ # CMakeLists in this exact order for cmake to work correctly cmake_minimum_required(VERSION 3.5) -idf_build_get_property(__hack_component_targets __COMPONENT_TARGETS) - -if("esp-nimble-component" IN_LIST BUILD_COMPONENTS OR "__esp-nimble-component" IN_LIST __hack_component_targets) +if(__COMPONENT_TARGETS MATCHES "___idf_esp-nimble-component") list(APPEND ESP_NIMBLE_PRIV_REQUIRES 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 nimble ) 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 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 framework-arduinoespressif32 ) @@ -30,27 +35,33 @@ idf_component_register( "esp32s3" "esp32c2" "esp32c3" + "esp32c5" "esp32c6" "esp32h2" + "esp32p4" INCLUDE_DIRS "src" SRCS "src/NimBLE2904.cpp" "src/NimBLEAddress.cpp" "src/NimBLEAdvertisedDevice.cpp" + "src/NimBLEAdvertisementData.cpp" "src/NimBLEAdvertising.cpp" + "src/NimBLEAttValue.cpp" "src/NimBLEBeacon.cpp" "src/NimBLECharacteristic.cpp" "src/NimBLEClient.cpp" "src/NimBLEDescriptor.cpp" "src/NimBLEDevice.cpp" "src/NimBLEEddystoneTLM.cpp" - "src/NimBLEEddystoneURL.cpp" "src/NimBLEExtAdvertising.cpp" "src/NimBLEHIDDevice.cpp" + "src/NimBLEL2CAPChannel.cpp" + "src/NimBLEL2CAPServer.cpp" "src/NimBLERemoteCharacteristic.cpp" "src/NimBLERemoteDescriptor.cpp" "src/NimBLERemoteService.cpp" + "src/NimBLERemoteValueAttribute.cpp" "src/NimBLEScan.cpp" "src/NimBLEServer.cpp" "src/NimBLEService.cpp" diff --git a/lib/libesp32_div/esp-nimble-cpp/CMakeLists.txt_idf3 b/lib/libesp32_div/esp-nimble-cpp/CMakeLists.txt_idf3 deleted file mode 100644 index c548f9021..000000000 --- a/lib/libesp32_div/esp-nimble-cpp/CMakeLists.txt_idf3 +++ /dev/null @@ -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() diff --git a/lib/libesp32_div/esp-nimble-cpp/Kconfig b/lib/libesp32_div/esp-nimble-cpp/Kconfig index 0878176a8..cdd322791 100644 --- a/lib/libesp32_div/esp-nimble-cpp/Kconfig +++ b/lib/libesp32_div/esp-nimble-cpp/Kconfig @@ -26,6 +26,113 @@ config NIMBLE_CPP_LOG_LEVEL default 3 if NIMBLE_CPP_LOG_LEVEL_INFO 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 bool "Show NimBLE return codes as text in debug log." default "n" @@ -50,6 +157,20 @@ config NIMBLE_CPP_ENABLE_ADVERTISEMENT_TYPE_TEXT while scanning as text messages in the debug log. 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 bool "Enable timestamps to be stored with attribute values." default "n" @@ -76,4 +197,58 @@ config NIMBLE_CPP_DEBUG_ASSERT_ENABLED Enabling this option will add debug asserts to the NimBLE CPP library. 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 \ No newline at end of file diff --git a/lib/libesp32_div/esp-nimble-cpp/LICENSE b/lib/libesp32_div/esp-nimble-cpp/LICENSE index ff162769f..a9063b320 100644 --- a/lib/libesp32_div/esp-nimble-cpp/LICENSE +++ b/lib/libesp32_div/esp-nimble-cpp/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier 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"); 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. See the License for the specific language governing permissions and limitations under the License. - - This product partly derives from esp32-snippets; Copyright 2017 Neil Kolban. \ No newline at end of file diff --git a/lib/libesp32_div/esp-nimble-cpp/NOTICE b/lib/libesp32_div/esp-nimble-cpp/NOTICE new file mode 100644 index 000000000..d4ce49cd0 --- /dev/null +++ b/lib/libesp32_div/esp-nimble-cpp/NOTICE @@ -0,0 +1,10 @@ +esp-nimble-cpp +NimBLE-Arduino +Copyright 2020-2025 Ryan Powell 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. diff --git a/lib/libesp32_div/esp-nimble-cpp/README.md b/lib/libesp32_div/esp-nimble-cpp/README.md index 7f37effad..bacf985d5 100644 --- a/lib/libesp32_div/esp-nimble-cpp/README.md +++ b/lib/libesp32_div/esp-nimble-cpp/README.md @@ -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/) -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] +> 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 -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)** This library **significantly** reduces resource usage and improves performance for ESP32 BLE applications as compared with the bluedroid based library. The goal is to maintain, as much as reasonable, compatibility with the original -library but refactored to use the NimBLE stack. In addition, this library will be more actively developed and maintained -to provide improved capabilites and stability over the original. +library but using the NimBLE stack. In addition, this library will be more actively developed and maintained +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!** *Your results may vary* @@ -35,16 +36,6 @@ Configure settings in `NimBLE Options`. Call `NimBLEDevice::init("");` in `app_main`.
-### 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`. -
- # Using 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.
+# Sponsors +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. +
+ # Acknowledgments * [nkolban](https://github.com/nkolban) and [chegewara](https://github.com/chegewara) for the [original esp32 BLE library](https://github.com/nkolban/esp32-snippets/tree/master/cpp_utils) this project was derived from. * [beegee-tokyo](https://github.com/beegee-tokyo) for contributing your time to test/debug and contributing the beacon examples. diff --git a/lib/libesp32_div/esp-nimble-cpp/component.mk b/lib/libesp32_div/esp-nimble-cpp/component.mk deleted file mode 100644 index 563436815..000000000 --- a/lib/libesp32_div/esp-nimble-cpp/component.mk +++ /dev/null @@ -1,2 +0,0 @@ -COMPONENT_ADD_INCLUDEDIRS := src -COMPONENT_SRCDIRS := src \ No newline at end of file diff --git a/lib/libesp32_div/esp-nimble-cpp/docs/1.x_to2.x_migration_guide.md b/lib/libesp32_div/esp-nimble-cpp/docs/1.x_to2.x_migration_guide.md new file mode 100644 index 000000000..a35255961 --- /dev/null +++ b/lib/libesp32_div/esp-nimble-cpp/docs/1.x_to2.x_migration_guide.md @@ -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) +
+ +## 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)`. +
+ +## 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`. +
+ +## 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`. +
+ +## 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. +
+ +## 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. +
+ +### Services +- `NimBLEService::getCharacteristics` now returns a `const std::vector&` instead of a copy of the vector. +
+ +### Characteristics +- `NimBLECharacteristic::notify` no longer takes a `bool is_notification` parameter, instead `NimBLECharacteristic::indicate` should be called to send indications. +
+ +#### 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. +
+ +### 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. +
+ +## Client +- `NimBLEClient::getServices` now returns a const reference to std::vector 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. +
+ +### 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`. +
+ +### Remote Services +- `NimBLERemoteService::getCharacteristics` now returns a `const std::vector&` instead of non-const `std::vector*`. +
+ +### 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`. +
+ +## 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. +
+ +### Advertised Device +- `NimBLEAdvertisedDevice::hasRSSI` removed as redundant, RSSI is always available. +- `NimBLEAdvertisedDevice::getPayload` now returns `const std::vector&` instead of a pointer to internal memory. +- `NimBLEAdvertisedDevice` Timestamp removed, if needed then the app should track the time from the callback. +
+ +## 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. +
+ +## 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`. +
+ +## Utilities +- `NimBLEUtils::dumpGapEvent` function removed. +- `NimBLEUtils::buildHexData` replaced with `NimBLEUtils::dataToHexString`, which returns a `std::string` containing the hex string. +
diff --git a/lib/libesp32_div/esp-nimble-cpp/docs/Doxyfile b/lib/libesp32_div/esp-nimble-cpp/docs/Doxyfile index c67e49f5a..70a835742 100644 --- a/lib/libesp32_div/esp-nimble-cpp/docs/Doxyfile +++ b/lib/libesp32_div/esp-nimble-cpp/docs/Doxyfile @@ -1,4 +1,4 @@ -# Doxyfile 1.9.5 +# Doxyfile 1.10.0 # This file describes the settings to be used by the documentation system # 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 # 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 # for a project that appears at the top of each page and should give viewer a @@ -63,6 +63,12 @@ PROJECT_BRIEF = 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 # 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 @@ -86,7 +92,7 @@ CREATE_SUBDIRS = NO # level increment doubles the number of directories, resulting in 4096 # 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 -# numer of 16 directories. +# number of 16 directories. # Minimum value: 0, maximum value: 8, default value: 8. # This tag requires that the tag CREATE_SUBDIRS is set to YES. @@ -363,6 +369,17 @@ MARKDOWN_SUPPORT = YES 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 # 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 @@ -487,6 +504,14 @@ LOOKUP_CACHE_SIZE = 0 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 #--------------------------------------------------------------------------- @@ -568,7 +593,8 @@ HIDE_UNDOC_MEMBERS = YES # 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 # 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. HIDE_UNDOC_CLASSES = YES @@ -859,14 +885,29 @@ WARN_IF_INCOMPLETE_DOC = YES 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 # 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 # 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. -WARN_AS_ERROR = YES +WARN_AS_ERROR = NO # 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 @@ -943,12 +984,12 @@ INPUT_FILE_ENCODING = # Note the list of default checked file patterns might differ from the list of # default file extension mappings. # -# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, -# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, -# *.hh, *.hxx, *.hpp, *.h++, *.l, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, -# *.inc, *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C -# comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd, -# *.vhdl, *.ucf, *.qsf and *.ice. +# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cxxm, +# *.cpp, *.cppm, *.ccm, *.c++, *.c++m, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, +# *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, *.h++, *.ixx, *.l, *.cs, *.d, +# *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, *.md, *.mm, *.dox (to +# be provided as doxygen C comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, +# *.f18, *.f, *.for, *.vhd, *.vhdl, *.ucf, *.qsf and *.ice. FILE_PATTERNS = *.c \ *.cc \ @@ -1034,9 +1075,6 @@ EXCLUDE_PATTERNS = # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, # 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 = @@ -1150,7 +1188,8 @@ FORTRAN_COMMENT_AFTER = 72 SOURCE_BROWSER = NO # 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. INLINE_SOURCES = NO @@ -1222,46 +1261,6 @@ USE_HTAGS = NO 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 #--------------------------------------------------------------------------- @@ -1273,10 +1272,11 @@ CLANG_DATABASE_PATH = ALPHABETICAL_INDEX = YES -# In case all classes in a project start with a common prefix, all classes will -# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag -# can be used to specify a prefix (or a list of prefixes) that should be ignored -# while generating the index headers. +# The IGNORE_PREFIX tag can be used to specify a prefix (or a list of prefixes) +# that should be ignored while generating the index headers. The IGNORE_PREFIX +# tag works for classes, function and member names. The entity will be placed in +# 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. IGNORE_PREFIX = @@ -1355,7 +1355,12 @@ HTML_STYLESHEET = # 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 # 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. HTML_EXTRA_STYLESHEET = @@ -1371,17 +1376,13 @@ HTML_EXTRA_STYLESHEET = HTML_EXTRA_FILES = # 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 -# enables light output unless the user preference is dark output. Other options -# are DARK to always use dark mode, LIGHT to always use light mode, AUTO_DARK to -# default to dark mode unless the user prefers light mode, and TOGGLE to let the -# user toggle between dark and light mode via a button. -# Possible values are: LIGHT Always generate light output., DARK Always generate -# dark output., AUTO_LIGHT Automatically set the mode according to the user -# 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.. +# should be rendered with a dark or light theme. +# Possible values are: LIGHT always generate light mode output, DARK always +# generate dark mode output, AUTO_LIGHT automatically set the mode according to +# the user 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. # This tag requires that the tag GENERATE_HTML is set to YES. @@ -1417,15 +1418,6 @@ HTML_COLORSTYLE_SAT = 100 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 # documentation will contain a main index with vertical navigation menus that # are dynamically created via JavaScript. If disabled, the navigation index will @@ -1445,6 +1437,33 @@ HTML_DYNAMIC_MENUS = YES 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 # shown in the various tree structured indices initially; the user can expand # and collapse entries dynamically later on. Doxygen will expand the tree to @@ -1575,6 +1594,16 @@ BINARY_TOC = 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 # 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 @@ -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 # 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 -# #tex-and-latex-extensions): +# for MathJax version 2 (see +# https://docs.mathjax.org/en/v2.7-latest/tex.html#tex-and-latex-extensions): # MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols # For example for MathJax version 3 (see # http://docs.mathjax.org/en/latest/input/tex/extensions/index.html): @@ -2063,9 +2092,16 @@ PDF_HYPERLINKS = YES USE_PDFLATEX = YES -# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode -# command to the generated LaTeX files. This will instruct LaTeX to keep running -# if errors occur, instead of asking the user for help. +# The LATEX_BATCHMODE tag signals the behavior of LaTeX in case of an error. +# Possible values are: NO same as ERROR_STOP, YES same as BATCH, BATCH In batch +# mode nothing is printed on the terminal, errors are scrolled as if 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. # This tag requires that the tag GENERATE_LATEX is set to YES. @@ -2086,14 +2122,6 @@ LATEX_HIDE_INDICES = NO 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) # 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 @@ -2259,7 +2287,7 @@ DOCBOOK_OUTPUT = docbook #--------------------------------------------------------------------------- # 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 # is still experimental and incomplete at the moment. # The default value is: NO. @@ -2270,6 +2298,28 @@ GENERATE_AUTOGEN_DEF = NO # 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 #--------------------------------------------------------------------------- @@ -2418,15 +2468,15 @@ TAGFILES = GENERATE_TAGFILE = -# If the ALLEXTERNALS tag is set to YES, all external class will be listed in -# the class index. If set to NO, only the inherited external classes will be -# listed. +# If the ALLEXTERNALS tag is set to YES, all external classes and namespaces +# will be listed in the class and namespace index. If set to NO, only the +# inherited external classes will be listed. # The default value is: NO. ALLEXTERNALS = NO # 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. # The default value is: YES. @@ -2440,16 +2490,9 @@ EXTERNAL_GROUPS = 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 # and usage relations if the target is undocumented or is not a class. # 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 # 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 # set to NO # The default value is: NO. @@ -2511,13 +2554,19 @@ DOT_NODE_ATTR = "shape=box,height=0.2,width=0.4" DOT_FONTPATH = -# If the CLASS_GRAPH tag is set to YES (or GRAPH) then doxygen will generate a -# graph for each documented class showing the direct and indirect inheritance -# relations. In case HAVE_DOT is set as well dot will be used to draw the graph, -# otherwise the built-in generator will be used. If the CLASS_GRAPH tag is set -# to TEXT the direct and indirect inheritance relations will be shown as texts / -# links. -# Possible values are: NO, YES, TEXT and GRAPH. +# If the CLASS_GRAPH tag is set to YES or GRAPH or BUILTIN then doxygen will +# generate a graph for each documented class showing the direct and indirect +# inheritance relations. In case the CLASS_GRAPH tag is set to YES or GRAPH and +# HAVE_DOT is enabled as well, then dot will be used to draw the graph. In case +# the CLASS_GRAPH tag is set to YES and HAVE_DOT is disabled or if the +# CLASS_GRAPH tag is set to BUILTIN, then the built-in generator will be used. +# 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. CLASS_GRAPH = TEXT @@ -2525,15 +2574,21 @@ CLASS_GRAPH = TEXT # 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 # 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. # This tag requires that the tag HAVE_DOT is set to YES. COLLABORATION_GRAPH = YES # 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 -# in the manual. +# groups, showing the direct groups dependencies. Explicit enabling a group +# 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. # 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 # 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 -# to avoid ugly line breaks. +# significantly it will be wrapped across multiple lines. Some heuristics are +# applied to avoid ugly line breaks. # Minimum value: 0, maximum value: 1000, default value: 17. # 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 # YES then doxygen will generate a graph for each documented file showing the # 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. # 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 # 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 -# 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. # 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 # dependencies a directory has on other directories in a graphical way. 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. # 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 # generated by dot. For an explanation of the image formats see the section # 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 # to make the SVG files visible in IE 9+ (other browsers do not have this # requirement). @@ -2695,11 +2758,12 @@ DOT_PATH = DOTFILE_DIRS = -# 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). +# 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. -MSCFILE_DIRS = +DIA_PATH = # 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 @@ -2776,3 +2840,19 @@ GENERATE_LEGEND = YES # The default value is: 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 +# -o . 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 = diff --git a/lib/libesp32_div/esp-nimble-cpp/docs/Improvements_and_updates.md b/lib/libesp32_div/esp-nimble-cpp/docs/Improvements_and_updates.md deleted file mode 100644 index a7504d5fb..000000000 --- a/lib/libesp32_div/esp-nimble-cpp/docs/Improvements_and_updates.md +++ /dev/null @@ -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) -
- - -# 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. -
- -`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(×tamp); // timestamp optional -``` -
- -**Advertising will automatically start when a client disconnects.** - -A new method `NimBLEServer::advertiseOnDisconnect(bool)` has been implemented to control this, true(default) = enabled. -
- -`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`. -
- - -# 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. -
- - -# 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(×tamp); // timestamp optional -``` -
- -`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. -
- -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(&::, this, _1, _2, _3, _4); - -->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. -
- -`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()`. - - -# 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) - diff --git a/lib/libesp32_div/esp-nimble-cpp/docs/Migration_guide.md b/lib/libesp32_div/esp-nimble-cpp/docs/Migration_guide.md index d1fcee8ad..3b82921ac 100644 --- a/lib/libesp32_div/esp-nimble-cpp/docs/Migration_guide.md +++ b/lib/libesp32_div/esp-nimble-cpp/docs/Migration_guide.md @@ -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. -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) * [Server](#server-api) @@ -20,12 +20,11 @@ For more information on the improvements and additions please refer to the [clas * [Remote characteristics](#remote-characteristics) * [Client Callbacks](#client-callbacks) * [Security](#client-security) -* [Scanning](#scan-api) +* [BLE scan](#ble-scan) * [General Security](#security-api) * [Configuration](#arduino-configuration)
- ## General Information ### 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. -`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.
- ## Server API Creating a `BLEServer` instance is the same as original, no changes required. For example `BLEDevice::createServer()` will work just as it did before. @@ -72,7 +70,7 @@ void onDisconnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo, int reason)` ```
-`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) @@ -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.
- ### Services Creating a `BLEService` (`NimBLEService`) instance is the same as original, no changes required. For example `BLEServer::createService(SERVICE_UUID)` will work just as it did before.
- ### Characteristics `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( ```
- #### Characteristic callbacks + `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. @@ -168,7 +164,6 @@ my_struct_t myStruct = pChr->getValue(); ```
- ### Descriptors 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 to handle callback functionality and the client subscription status is handled internally. -**Note:** Attempting to create a 0x2902 descriptor will trigger an assert to notify the error, -allowing the creation of it would cause a fault in the NimBLE stack. +**Note:** Attempting to create a 0x2902 descriptor will trigger a warning message and flag it internally as removed and will not be functional. All other descriptors are now created just as characteristics are by using the `NimBLECharacteristic::createDescriptor` method (except 0x2904, see below). Which are defined as: @@ -197,7 +191,7 @@ NimBLEDescriptor* createDescriptor(NimBLEUUID uuid, NIMBLE_PROPERTY::WRITE, uint16_t max_len = 100); ``` -##### Example +#### Example ``` pDescriptor = pCharacteristic->createDescriptor("ABCD", 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.
-For the 0x2904, there is a special class that is created when you call `createDescriptor("2904"). - -The pointer returned is of the base class `NimBLEDescriptor` but the call will create the derived class of `NimBLE2904` so you must cast the returned pointer to -`NimBLE2904` to access the specific class methods. - -##### Example -``` -p2904 = (NimBLE2904*)pCharacteristic->createDescriptor("2904"); -``` +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.
- #### Descriptor callbacks > `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.
- ### Server Security Security is set on the characteristic or descriptor properties by applying one of the following: > 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.
- ## Advertising API 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) -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. +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.
- ## Client API 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. Defined as: -> NimBLEClient::connect(bool deleteServices = true); -> NimBLEClient::connect(NimBLEAdvertisedDevice\* device, bool deleteServices = true); -> NimBLEClient::connect(NimBLEAddress address, bool deleteServices = true); +> NimBLEClient::connect(bool deleteServices = true, , bool asyncConnect = false, bool exchangeMTU = true); +> NimBLEClient::connect(const NimBLEAddress& address, bool deleteAttributes = true, bool asyncConnect = false, bool exchangeMTU = 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. -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.
> `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.
- ### Remote Services `BLERemoteService` (`NimBLERemoteService`) Methods remain mostly unchanged with the exceptions of: @@ -301,12 +284,10 @@ This method has been removed. > `BLERemoteService::getCharacteristics` (`NimBLERemoteService::getCharacteristics`) -This method now takes an optional (bool) parameter to indicate if the characteristics should be retrieved from the server (true) or -the currently known database returned (false : default). +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. Also now returns a pointer to `std::vector` instead of `std::map`.
- ### Remote Characteristics `BLERemoteCharacteristic` (`NimBLERemoteCharacteristic`) 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.
-> `BLERemoteCharacteristic::readUInt8` (`NimBLERemoteCharacteristic::readUInt8`) -> `BLERemoteCharacteristic::readUInt16` (`NimBLERemoteCharacteristic::readUInt16`) -> `BLERemoteCharacteristic::readUInt32` (`NimBLERemoteCharacteristic::readUInt32`) +> `BLERemoteCharacteristic::readUInt8` (`NimBLERemoteCharacteristic::readUInt8`) +> `BLERemoteCharacteristic::readUInt16` (`NimBLERemoteCharacteristic::readUInt16`) +> `BLERemoteCharacteristic::readUInt32` (`NimBLERemoteCharacteristic::readUInt32`) > `BLERemoteCharacteristic::readFloat` (`NimBLERemoteCharacteristic::readFloat`) -Are **deprecated** a template: `NimBLERemoteCharacteristic::readValue(time_t\*, bool)` has been added to replace them. +Are **removed** a template: `NimBLERemoteCharacteristic::readValue(time_t\*, bool)` has been added to replace them.
> `BLERemoteCharacteristic::readRawData` @@ -339,10 +320,10 @@ Are **deprecated** a template: `NimBLERemoteCharacteristic::readValue(tim **Has been removed from the API** Originally it stored an unnecessary copy of the data and was returning a `uint8_t` pointer to volatile internal data. The user application should use `NimBLERemoteCharacteristic::readValue` or `NimBLERemoteCharacteristic::getValue`. -To 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(); -uint8_t *data = (uint8_t*)value.data(); +NimBLEAttValue value = pChr->readValue(); +const uint8_t *data = value.data(); ``` Alternatively use the `readValue` template: ``` @@ -352,12 +333,10 @@ my_struct_t myStruct = pChr->readValue(); > `BLERemoteCharacteristic::getDescriptors` (`NimBLERemoteCharacteristic::getDescriptors`) -This method now takes an optional (bool) parameter to indicate if the descriptors should be retrieved from the server (true) or -the currently known database returned (false : default). +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. Also now returns a pointer to `std::vector` instead of `std::map`.
- ### Client callbacks > `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.
- ### Client Security The client will automatically initiate security when the peripheral responds that it's required. The default configuration will use "just-works" pairing with no bonding, if you wish to enable bonding see below.
- ## 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.
- ## Security API Security operations have been moved to `BLEDevice` (`NimBLEDevice`). The security callback methods are now incorporated in the `NimBLEServerCallbacks` / `NimBLEClientCallbacks` classes. 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. -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.
-> `void onPassKeyEntry(const NimBLEConnInfo& connInfo)` +> `void onPassKeyEntry(NimBLEConnInfo& connInfo)` Client callback; client should respond with the passkey (pin) by calling `NimBLEDevice::injectPassKey(connInfo, 123456);`
@@ -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.
-> `void onAuthenticationComplete(const NimBLEConnInfo& connInfo)` +> `void onAuthenticationComplete(NimBLEConnInfo& connInfo)` Authentication complete, success or failed information is available from the `NimBLEConnInfo` methods.
@@ -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.
- ## 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. diff --git a/lib/libesp32_div/esp-nimble-cpp/docs/New_user_guide.md b/lib/libesp32_div/esp-nimble-cpp/docs/New_user_guide.md index 506a368b3..888102d45 100644 --- a/lib/libesp32_div/esp-nimble-cpp/docs/New_user_guide.md +++ b/lib/libesp32_div/esp-nimble-cpp/docs/New_user_guide.md @@ -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.
- ## 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. @@ -38,9 +37,7 @@ For this example we will keep it simple and use a 16 bit value: ABCD. ``` #include "NimBLEDevice.h" -// void setup() in Arduino -void app_main(void) -{ +extern "C" void app_main(void) { NimBLEDevice::init("NimBLE"); NimBLEServer *pServer = NimBLEDevice::createServer(); @@ -80,9 +77,7 @@ The function call will simply be `pService->createCharacteristic("1234");` ``` #include "NimBLEDevice.h" -// void setup() in Arduino -void app_main(void) -{ +extern "C" void app_main(void) { NimBLEDevice::init("NimBLE"); 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");` 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:** ``` 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 ``` 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" -// void setup() in Arduino -void app_main(void) -{ +extern "C" void app_main(void) { NimBLEDevice::init("NimBLE"); NimBLEServer *pServer = NimBLEDevice::createServer(); @@ -127,7 +121,8 @@ void app_main(void) pCharacteristic->setValue("Hello BLE"); 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(); } ``` @@ -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.
- ## 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. @@ -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. -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. 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" -// void setup() in Arduino -void app_main(void) -{ +extern "C" void app_main(void) { NimBLEDevice::init(""); 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. 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`. 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"); -for(int i = 0; i < results.getCount(); i++) { - NimBLEAdvertisedDevice device = results.getDevice(i); +for (int i = 0; i < results.getCount(); i++) { + const NimBLEAdvertisedDevice *device = results.getDevice(i); - if (device.isAdvertisingService(serviceUuid)) { - // create a client and connect + if (device->isAdvertisingService(serviceUuid)) { + // create a client and connect } } ``` @@ -200,16 +192,16 @@ This takes a pointer to the `NimBLEAdvertisedDevice` and returns `true` if succe ``` NimBLEUUID serviceUuid("ABCD"); -for(int i = 0; i < results.getCount(); i++) { - NimBLEAdvertisedDevice device = results.getDevice(i); +for (int i = 0; i < results.getCount(); i++) { + const NimBLEAdvertisedDevice *device = results.getDevice(i); - if (device.isAdvertisingService(serviceUuid)) { + if (device->isAdvertisingService(serviceUuid)) { NimBLEClient *pClient = NimBLEDevice::createClient(); - if(pClient->connect(&device)) { - //success + if (pClient->connect(&device)) { + //success } else { - // failed to connect + // failed to connect } } } @@ -231,11 +223,15 @@ Finally we will read the characteristic value with `NimBLERemoteCharacteristic:: ``` NimBLEUUID serviceUuid("ABCD"); -for(int i = 0; i < results.getCount(); i++) { - NimBLEAdvertisedDevice device = results.getDevice(i); +for (int i = 0; i < results.getCount(); i++) { + const NimBLEAdvertisedDevice *device = results.getDevice(i); - if (device.isAdvertisingService(serviceUuid)) { + if (device->isAdvertisingService(serviceUuid)) { NimBLEClient *pClient = NimBLEDevice::createClient(); + + if (!pClient) { // Make sure the client was created + break; + } if (pClient->connect(&device)) { NimBLERemoteService *pService = pClient->getService(serviceUuid); @@ -249,7 +245,7 @@ for(int i = 0; i < results.getCount(); i++) { } } } else { - // failed to connect + // failed to connect } } } @@ -264,12 +260,16 @@ This is done by calling `NimBLEDevice::deleteClient`. ``` NimBLEUUID serviceUuid("ABCD"); -for(int i = 0; i < results.getCount(); i++) { - NimBLEAdvertisedDevice device = results.getDevice(i); +for (int i = 0; i < results.getCount(); i++) { + const NimBLEAdvertisedDevice *device = results.getDevice(i); - if (device.isAdvertisingService(serviceUuid)) { + if (device->isAdvertisingService(serviceUuid)) { NimBLEClient *pClient = NimBLEDevice::createClient(); - + + if (!pClient) { // Make sure the client was created + break; + } + if (pClient->connect(&device)) { NimBLERemoteService *pService = pClient->getService(serviceUuid); @@ -282,7 +282,7 @@ for(int i = 0; i < results.getCount(); i++) { } } } else { - // failed to connect + // failed to connect } 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" -// void setup() in Arduino -void app_main(void) -{ +extern "C" void app_main(void) { NimBLEDevice::init(""); - + NimBLEScan *pScan = NimBLEDevice::getScan(); - NimBLEScanResults results = pScan->start(10 * 1000); - + NimBLEScanResults results = pScan->getResults(10 * 1000); + NimBLEUUID serviceUuid("ABCD"); - - for(int i = 0; i < results.getCount(); i++) { - NimBLEAdvertisedDevice device = results.getDevice(i); - - if (device.isAdvertisingService(serviceUuid)) { + + for (int i = 0; i < results.getCount(); i++) { + const NimBLEAdvertisedDevice *device = results.getDevice(i); + + if (device->isAdvertisingService(serviceUuid)) { NimBLEClient *pClient = NimBLEDevice::createClient(); - + + if (!pClient) { // Make sure the client was created + break; + } + if (pClient->connect(&device)) { NimBLERemoteService *pService = pClient->getService(serviceUuid); - + if (pService != nullptr) { NimBLERemoteCharacteristic *pCharacteristic = pService->getCharacteristic("1234"); - + if (pCharacteristic != nullptr) { std::string value = pCharacteristic->readValue(); // print or do whatever you need with the value } } } else { - // failed to connect + // failed to connect } - + 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.
- diff --git a/lib/libesp32_div/esp-nimble-cpp/docs/index.md b/lib/libesp32_div/esp-nimble-cpp/docs/index.md index 55764b854..570c0183c 100644 --- a/lib/libesp32_div/esp-nimble-cpp/docs/index.md +++ b/lib/libesp32_div/esp-nimble-cpp/docs/index.md @@ -1,5 +1,4 @@ # Overview - This is a C++ BLE library for the ESP32 that uses the NimBLE host stack instead of bluedroid. The aim is to maintain, as much as reasonable, the original bluedroid C++ & Arduino BLE API by while adding new features 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
# ESP-IDF Installation -### v4.0+ +## v4.0+ 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. @@ -23,34 +22,23 @@ Configure settings in `NimBLE Options`. Call `NimBLEDevice::init` in `app_main`.
-### 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`. -
- # Using 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 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. +If you are familiar with the original library, see: [The migration guide](Migration_guide.md) for details. For more advanced usage see [Usage tips](Usage_tips.md) for more performance and optimization.
-# Need help? Have a question or suggestion? -Come chat on [gitter](https://gitter.im/NimBLE-Arduino/community?utm_source=share-link&utm_medium=link&utm_campaign=share-link) or open an issue at [NimBLE-Arduino](https://github.com/h2zero/NimBLE-Arduino/issues) or [esp-nimble-cpp](https://github.com/h2zero/esp-nimble-cpp/issues) +# Sponsors +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.
# Acknowledgments - * [nkolban](https://github.com/nkolban) and [chegewara](https://github.com/chegewara) for the [original esp32 BLE library](https://github.com/nkolban/esp32-snippets/tree/master/cpp_utils) this project was derived from. * [beegee-tokyo](https://github.com/beegee-tokyo) for contributing your time to test/debug and contributing the beacon examples. * [Jeroen88](https://github.com/Jeroen88) for the amazing help debugging and improving the client code. diff --git a/lib/libesp32_div/esp-nimble-cpp/examples/Advanced/NimBLE_Client/Makefile b/lib/libesp32_div/esp-nimble-cpp/examples/Advanced/NimBLE_Client/Makefile deleted file mode 100644 index d1a0fa289..000000000 --- a/lib/libesp32_div/esp-nimble-cpp/examples/Advanced/NimBLE_Client/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -PROJECT_NAME := NimBLE_Client - -include $(IDF_PATH)/make/project.mk diff --git a/lib/libesp32_div/esp-nimble-cpp/examples/Advanced/NimBLE_Client/main/main.cpp b/lib/libesp32_div/esp-nimble-cpp/examples/Advanced/NimBLE_Client/main/main.cpp deleted file mode 100644 index fe76dd0d9..000000000 --- a/lib/libesp32_div/esp-nimble-cpp/examples/Advanced/NimBLE_Client/main/main.cpp +++ /dev/null @@ -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 - -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); -} - diff --git a/lib/libesp32_div/esp-nimble-cpp/examples/Advanced/NimBLE_Client/sdkconfig.defaults b/lib/libesp32_div/esp-nimble-cpp/examples/Advanced/NimBLE_Client/sdkconfig.defaults deleted file mode 100644 index c829fc5c0..000000000 --- a/lib/libesp32_div/esp-nimble-cpp/examples/Advanced/NimBLE_Client/sdkconfig.defaults +++ /dev/null @@ -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 diff --git a/lib/libesp32_div/esp-nimble-cpp/examples/Advanced/NimBLE_Server/Makefile b/lib/libesp32_div/esp-nimble-cpp/examples/Advanced/NimBLE_Server/Makefile deleted file mode 100644 index dd998b116..000000000 --- a/lib/libesp32_div/esp-nimble-cpp/examples/Advanced/NimBLE_Server/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -PROJECT_NAME := NimBLE_Server - -include $(IDF_PATH)/make/project.mk diff --git a/lib/libesp32_div/esp-nimble-cpp/examples/Advanced/NimBLE_Server/sdkconfig.defaults b/lib/libesp32_div/esp-nimble-cpp/examples/Advanced/NimBLE_Server/sdkconfig.defaults deleted file mode 100644 index c829fc5c0..000000000 --- a/lib/libesp32_div/esp-nimble-cpp/examples/Advanced/NimBLE_Server/sdkconfig.defaults +++ /dev/null @@ -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 diff --git a/lib/libesp32_div/esp-nimble-cpp/examples/Bluetooth_5/NimBLE_extended_client/CMakeLists.txt b/lib/libesp32_div/esp-nimble-cpp/examples/Bluetooth_5/NimBLE_extended_client/CMakeLists.txt index f46b44afd..c946003d5 100644 --- a/lib/libesp32_div/esp-nimble-cpp/examples/Bluetooth_5/NimBLE_extended_client/CMakeLists.txt +++ b/lib/libesp32_div/esp-nimble-cpp/examples/Bluetooth_5/NimBLE_extended_client/CMakeLists.txt @@ -3,5 +3,4 @@ cmake_minimum_required(VERSION 3.5) include($ENV{IDF_PATH}/tools/cmake/project.cmake) -set(SUPPORTED_TARGETS esp32c3 esp32s3) project(NimBLE_extended_client) diff --git a/lib/libesp32_div/esp-nimble-cpp/examples/Bluetooth_5/NimBLE_extended_client/Makefile b/lib/libesp32_div/esp-nimble-cpp/examples/Bluetooth_5/NimBLE_extended_client/Makefile deleted file mode 100644 index 2e4842d3f..000000000 --- a/lib/libesp32_div/esp-nimble-cpp/examples/Bluetooth_5/NimBLE_extended_client/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -PROJECT_NAME := NimBLE_extended_client - -include $(IDF_PATH)/make/project.mk diff --git a/lib/libesp32_div/esp-nimble-cpp/examples/Bluetooth_5/NimBLE_extended_client/main/component.mk b/lib/libesp32_div/esp-nimble-cpp/examples/Bluetooth_5/NimBLE_extended_client/main/component.mk deleted file mode 100644 index a98f634ea..000000000 --- a/lib/libesp32_div/esp-nimble-cpp/examples/Bluetooth_5/NimBLE_extended_client/main/component.mk +++ /dev/null @@ -1,4 +0,0 @@ -# -# "main" pseudo-component makefile. -# -# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) diff --git a/lib/libesp32_div/esp-nimble-cpp/examples/Bluetooth_5/NimBLE_extended_client/main/main.cpp b/lib/libesp32_div/esp-nimble-cpp/examples/Bluetooth_5/NimBLE_extended_client/main/main.cpp index 3572e59ef..9248d94d1 100644 --- a/lib/libesp32_div/esp-nimble-cpp/examples/Bluetooth_5/NimBLE_extended_client/main/main.cpp +++ b/lib/libesp32_div/esp-nimble-cpp/examples/Bluetooth_5/NimBLE_extended_client/main/main.cpp @@ -1,71 +1,62 @@ -/** NimBLE Extended Client Demo: +/** + * NimBLE Extended Client Demo: * * Demonstrates the Bluetooth 5.x client capabilities. * * Created: on April 2 2022 * Author: H2zero - * -*/ -#include + */ -extern "C" void app_main(void); +#include #define SERVICE_UUID "ABCD" #define CHARACTERISTIC_UUID "1234" -static NimBLEAdvertisedDevice* advDevice; -static bool doConnect = false; -static uint32_t scanTime = 10 * 1000; // In milliseconds, 0 = scan forever +static const NimBLEAdvertisedDevice* advDevice; +static bool doConnect = false; +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).*/ -static uint8_t connectPhys = BLE_GAP_LE_PHY_CODED_MASK | BLE_GAP_LE_PHY_1M_MASK /*| BLE_GAP_LE_PHY_2M_MASK */ ; +/** 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 */; -/* 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 { - void onConnect(NimBLEClient* pClient) { - printf("Connected\n"); - }; + void onConnect(NimBLEClient* pClient) override { printf("Connected\n"); }; - void onDisconnect(NimBLEClient* pClient, int reason) { - printf("%s Disconnected, reason = %d - Starting scan\n", - pClient->getPeerAddress().toString().c_str(), reason); - NimBLEDevice::getScan()->start(scanTime); - }; -}; + 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; - -/* Define a class to handle the callbacks when advertisements are received */ -class scanCallbacks: public NimBLEScanCallbacks { - void onResult(NimBLEAdvertisedDevice* advertisedDevice) { +/** Define a class to handle the callbacks when advertisements 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("ABCD"))) - { + if (advertisedDevice->isAdvertisingService(NimBLEUUID("ABCD"))) { printf("Found Our Service\n"); - /* Ready to connect now */ 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; - /* stop scan before connecting */ + /** stop scan before connecting */ NimBLEDevice::getScan()->stop(); } } /** Callback to process the results of the completed scan or restart it */ - void onScanEnd(NimBLEScanResults results) { - printf("Scan Ended\n"); - } -}; + void onScanEnd(const NimBLEScanResults& results, int rc) override { 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() { NimBLEClient* pClient = nullptr; 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 * * 0x02 BLE_GAP_LE_PHY_2M_MASK * * 0x04 BLE_GAP_LE_PHY_CODED_MASK @@ -77,27 +68,22 @@ bool connectToServer() { pClient->setConnectTimeout(10 * 1000); 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); printf("Failed to connect, deleted client\n"); return false; } - printf("Connected to: %s RSSI: %d\n", - pClient->getPeerAddress().toString().c_str(), - pClient->getRssi()); + 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; + /** Now we can read/write/subscribe the characteristics of the services we are interested in */ + NimBLERemoteService* pSvc = nullptr; NimBLERemoteCharacteristic* pChr = nullptr; pSvc = pClient->getService(SERVICE_UUID); - if (pSvc) { pChr = pSvc->getCharacteristic(CHARACTERISTIC_UUID); - if (pChr) { - // Read the value of the characteristic. if (pChr->canRead()) { std::string value = pChr->readValue(); printf("Characteristic value: %s\n", value.c_str()); @@ -113,11 +99,37 @@ bool connectToServer() { return true; } -void connectTask (void * parameter){ - /* Loop here until we find a device we want to connect to */ +extern "C" void app_main(void) { + 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 (;;) { if (doConnect) { - /* Found a device we want to connect to, do it now */ if (connectToServer()) { printf("Success!, scanning for more!\n"); } else { @@ -125,39 +137,8 @@ void connectTask (void * parameter){ } doConnect = false; - NimBLEDevice::getScan()->start(scanTime); + NimBLEDevice::getScan()->start(scanTimeMs); } 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"); } diff --git a/lib/libesp32_div/esp-nimble-cpp/examples/basic/BLE_uart/CMakeLists.txt b/lib/libesp32_div/esp-nimble-cpp/examples/Bluetooth_5/NimBLE_extended_scan/CMakeLists.txt similarity index 81% rename from lib/libesp32_div/esp-nimble-cpp/examples/basic/BLE_uart/CMakeLists.txt rename to lib/libesp32_div/esp-nimble-cpp/examples/Bluetooth_5/NimBLE_extended_scan/CMakeLists.txt index 9060e47fc..6b89fd9a6 100644 --- a/lib/libesp32_div/esp-nimble-cpp/examples/basic/BLE_uart/CMakeLists.txt +++ b/lib/libesp32_div/esp-nimble-cpp/examples/Bluetooth_5/NimBLE_extended_scan/CMakeLists.txt @@ -3,5 +3,4 @@ cmake_minimum_required(VERSION 3.5) include($ENV{IDF_PATH}/tools/cmake/project.cmake) -set(SUPPORTED_TARGETS esp32) -project(BLE_uart) +project(NimBLE_extended_scan) diff --git a/lib/libesp32_div/esp-nimble-cpp/examples/NimBLE_server_get_client_name/main/CMakeLists.txt b/lib/libesp32_div/esp-nimble-cpp/examples/Bluetooth_5/NimBLE_extended_scan/main/CMakeLists.txt similarity index 100% rename from lib/libesp32_div/esp-nimble-cpp/examples/NimBLE_server_get_client_name/main/CMakeLists.txt rename to lib/libesp32_div/esp-nimble-cpp/examples/Bluetooth_5/NimBLE_extended_scan/main/CMakeLists.txt diff --git a/lib/libesp32_div/esp-nimble-cpp/examples/Bluetooth_5/NimBLE_extended_scan/main/main.cpp b/lib/libesp32_div/esp-nimble-cpp/examples/Bluetooth_5/NimBLE_extended_scan/main/main.cpp new file mode 100644 index 000000000..61b0b0cc5 --- /dev/null +++ b/lib/libesp32_div/esp-nimble-cpp/examples/Bluetooth_5/NimBLE_extended_scan/main/main.cpp @@ -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 + +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"); +} diff --git a/lib/libesp32_div/esp-nimble-cpp/examples/Bluetooth_5/NimBLE_extended_server/CMakeLists.txt b/lib/libesp32_div/esp-nimble-cpp/examples/Bluetooth_5/NimBLE_extended_server/CMakeLists.txt index c58174ac7..0fdffce0b 100644 --- a/lib/libesp32_div/esp-nimble-cpp/examples/Bluetooth_5/NimBLE_extended_server/CMakeLists.txt +++ b/lib/libesp32_div/esp-nimble-cpp/examples/Bluetooth_5/NimBLE_extended_server/CMakeLists.txt @@ -3,5 +3,4 @@ cmake_minimum_required(VERSION 3.5) include($ENV{IDF_PATH}/tools/cmake/project.cmake) -set(SUPPORTED_TARGETS esp32c3 esp32s3) project(NimBLE_extended_server) diff --git a/lib/libesp32_div/esp-nimble-cpp/examples/Bluetooth_5/NimBLE_extended_server/Makefile b/lib/libesp32_div/esp-nimble-cpp/examples/Bluetooth_5/NimBLE_extended_server/Makefile deleted file mode 100644 index a18cf9fad..000000000 --- a/lib/libesp32_div/esp-nimble-cpp/examples/Bluetooth_5/NimBLE_extended_server/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -PROJECT_NAME := NimBLE_extended_server - -include $(IDF_PATH)/make/project.mk diff --git a/lib/libesp32_div/esp-nimble-cpp/examples/Bluetooth_5/NimBLE_extended_server/main/component.mk b/lib/libesp32_div/esp-nimble-cpp/examples/Bluetooth_5/NimBLE_extended_server/main/component.mk deleted file mode 100644 index a98f634ea..000000000 --- a/lib/libesp32_div/esp-nimble-cpp/examples/Bluetooth_5/NimBLE_extended_server/main/component.mk +++ /dev/null @@ -1,4 +0,0 @@ -# -# "main" pseudo-component makefile. -# -# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) diff --git a/lib/libesp32_div/esp-nimble-cpp/examples/Bluetooth_5/NimBLE_extended_server/main/main.cpp b/lib/libesp32_div/esp-nimble-cpp/examples/Bluetooth_5/NimBLE_extended_server/main/main.cpp index 9ad5f5902..607cd02ea 100644 --- a/lib/libesp32_div/esp-nimble-cpp/examples/Bluetooth_5/NimBLE_extended_server/main/main.cpp +++ b/lib/libesp32_div/esp-nimble-cpp/examples/Bluetooth_5/NimBLE_extended_server/main/main.cpp @@ -1,4 +1,5 @@ -/** NimBLE Extended Server Demo: +/** + * NimBLE Extended Server Demo: * * Demonstrates the Bluetooth 5.x extended advertising capabilities. * @@ -9,55 +10,52 @@ * * Created: on April 2 2022 * Author: H2zero - * -*/ + */ -#include "NimBLEDevice.h" -#include "esp_sleep.h" - -extern "C" void app_main(void); +#include +#include #define SERVICE_UUID "ABCD" #define CHARACTERISTIC_UUID "1234" -/* Time in milliseconds to advertise */ +/** Time in milliseconds to advertise */ static uint32_t advTime = 5000; -/* Time to sleep between advertisements */ +/** Time to sleep between advertisements */ 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; -/* 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; - -/* Handler class for server events */ -class ServerCallbacks: public NimBLEServerCallbacks { - void onConnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo) { +/** Handler class for server events */ +class ServerCallbacks : public NimBLEServerCallbacks { + void onConnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo) override { printf("Client connected:: %s\n", connInfo.getAddress().toString().c_str()); - }; + } - void onDisconnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo, int reason) { - printf("Client disconnected - sleeping for %" PRIu32" seconds\n", sleepSeconds); + void onDisconnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo, int reason) override { + printf("Client disconnected - sleeping for %" PRIu32 " seconds\n", sleepSeconds); esp_deep_sleep_start(); - }; -}; + } +} serverCallbacks; -/* Callback class to handle advertising events */ -class advertisingCallbacks: public NimBLEExtAdvertisingCallbacks { - void onStopped(NimBLEExtAdvertising* pAdv, int reason, uint8_t inst_id) { +/** Callback class to handle advertising events */ +class AdvertisingCallbacks : public NimBLEExtAdvertisingCallbacks { + void onStopped(NimBLEExtAdvertising* pAdv, int reason, uint8_t instId) override { /* 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) { case 0: printf("Client connecting\n"); return; case BLE_HS_ETIMEOUT: - printf("Time expired - sleeping for %" PRIu32" seconds\n", sleepSeconds); + printf("Time expired - sleeping for %" PRIu32 " seconds\n", sleepSeconds); break; default: break; @@ -65,66 +63,67 @@ class advertisingCallbacks: public NimBLEExtAdvertisingCallbacks { 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"); - /* Create the server and add the services/characteristics/descriptors */ - NimBLEServer *pServer = NimBLEDevice::createServer(); - pServer->setCallbacks(new ServerCallbacks); + /** Create the server and add the services/characteristics/descriptors */ + NimBLEServer* pServer = NimBLEDevice::createServer(); + pServer->setCallbacks(&serverCallbacks); - NimBLEService *pService = pServer->createService(SERVICE_UUID); - NimBLECharacteristic *pCharacteristic = pService->createCharacteristic(CHARACTERISTIC_UUID, - NIMBLE_PROPERTY::READ | - NIMBLE_PROPERTY::WRITE | - NIMBLE_PROPERTY::NOTIFY); + NimBLEService* pService = pServer->createService(SERVICE_UUID); + NimBLECharacteristic* pCharacteristic = + pService->createCharacteristic(CHARACTERISTIC_UUID, + NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::NOTIFY); pCharacteristic->setValue("Hello World"); - /* Start the services */ + /** Start the service */ pService->start(); - /* - * 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. - */ + /** + * 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. + */ NimBLEExtAdvertisement extAdv(primaryPhy, secondaryPhy); - /* Set the advertisement as connectable */ + /** Set the advertisement as connectable */ 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. - /* 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" - "Extended advertising allows for " - "251 bytes of data in a single advertisement,\r\n" - "or up to 1650 bytes with chaining.\r\n" - "This example message is 226 bytes long " - "and is using CODED_PHY for long range.")); + /** 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" + "Extended advertising allows for " + "251 bytes of data in a single advertisement,\r\n" + "or up to 1650 bytes with chaining.\r\n" + "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(); - /* Set the callbacks for advertising events */ - pAdvertising->setCallbacks(new advertisingCallbacks); + /** Set the callbacks for advertising events */ + pAdvertising->setCallbacks(&advertisingCallbacks); - /* - * NimBLEExtAdvertising::setInstanceData takes the instance ID and - * a reference to a `NimBLEExtAdvertisement` object. This sets the data - * that will be advertised for this instance ID, returns true if successful. + /** + * NimBLEExtAdvertising::setInstanceData takes the instance ID and + * a reference to a `NimBLEExtAdvertisement` object. This sets the data + * 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 - * is called before exiting the code block as the data will be copied. + * 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. */ if (pAdvertising->setInstanceData(0, extAdv)) { - /* - * `NimBLEExtAdvertising::start` takes the advertisement instance ID to start - * and a duration in milliseconds or a max number of advertisements to send (or both). + /** + * NimBLEExtAdvertising::start takes the advertisement instance ID to start + * and a duration in milliseconds or a max number of advertisements to send (or both). */ if (pAdvertising->start(0, advTime)) { printf("Started advertising\n"); @@ -132,7 +131,7 @@ void app_main (void) { printf("Failed to start advertising\n"); } } else { - printf("Failed to register advertisment data\n"); + printf("Failed to register advertisement data\n"); } esp_sleep_enable_timer_wakeup(sleepSeconds * 1000000); diff --git a/lib/libesp32_div/esp-nimble-cpp/examples/Bluetooth_5/NimBLE_multi_advertiser/CMakeLists.txt b/lib/libesp32_div/esp-nimble-cpp/examples/Bluetooth_5/NimBLE_multi_advertiser/CMakeLists.txt index 7cfce8676..5eac80778 100644 --- a/lib/libesp32_div/esp-nimble-cpp/examples/Bluetooth_5/NimBLE_multi_advertiser/CMakeLists.txt +++ b/lib/libesp32_div/esp-nimble-cpp/examples/Bluetooth_5/NimBLE_multi_advertiser/CMakeLists.txt @@ -3,5 +3,4 @@ cmake_minimum_required(VERSION 3.5) include($ENV{IDF_PATH}/tools/cmake/project.cmake) -set(SUPPORTED_TARGETS esp32c3 esp32s3) project(NimBLE_multi_advertiser) diff --git a/lib/libesp32_div/esp-nimble-cpp/examples/Bluetooth_5/NimBLE_multi_advertiser/Makefile b/lib/libesp32_div/esp-nimble-cpp/examples/Bluetooth_5/NimBLE_multi_advertiser/Makefile deleted file mode 100644 index 501edc99d..000000000 --- a/lib/libesp32_div/esp-nimble-cpp/examples/Bluetooth_5/NimBLE_multi_advertiser/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -PROJECT_NAME := NimBLE_multi_advertiser - -include $(IDF_PATH)/make/project.mk diff --git a/lib/libesp32_div/esp-nimble-cpp/examples/Bluetooth_5/NimBLE_multi_advertiser/main/component.mk b/lib/libesp32_div/esp-nimble-cpp/examples/Bluetooth_5/NimBLE_multi_advertiser/main/component.mk deleted file mode 100644 index a98f634ea..000000000 --- a/lib/libesp32_div/esp-nimble-cpp/examples/Bluetooth_5/NimBLE_multi_advertiser/main/component.mk +++ /dev/null @@ -1,4 +0,0 @@ -# -# "main" pseudo-component makefile. -# -# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) diff --git a/lib/libesp32_div/esp-nimble-cpp/examples/Bluetooth_5/NimBLE_multi_advertiser/main/main.cpp b/lib/libesp32_div/esp-nimble-cpp/examples/Bluetooth_5/NimBLE_multi_advertiser/main/main.cpp index d7eba8a6b..2269ecb8a 100644 --- a/lib/libesp32_div/esp-nimble-cpp/examples/Bluetooth_5/NimBLE_multi_advertiser/main/main.cpp +++ b/lib/libesp32_div/esp-nimble-cpp/examples/Bluetooth_5/NimBLE_multi_advertiser/main/main.cpp @@ -1,4 +1,5 @@ -/** NimBLE Multi Advertiser Demo: +/** + * NimBLE Multi Advertiser Demo: * * Demonstrates the Bluetooth 5.x extended advertising capabilities. * @@ -9,59 +10,56 @@ * * Created: on April 9 2022 * Author: H2zero - * -*/ + */ -#include "NimBLEDevice.h" -#include "esp_sleep.h" - -extern "C" void app_main(void); +#include +#include #define SERVICE_UUID "ABCD" #define CHARACTERISTIC_UUID "1234" -/* Time in milliseconds to advertise */ +/** Time in milliseconds to advertise */ static uint32_t advTime = 5000; -/* Time to sleep between advertisements */ +/** Time to sleep between advertisements */ 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; -/* 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; - -/* Handler class for server events */ -class ServerCallbacks: public NimBLEServerCallbacks { - void onConnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo) { +/** Handler class for server events */ +class ServerCallbacks : public NimBLEServerCallbacks { + void onConnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo) override { 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"); // if still advertising we won't sleep yet. if (!pServer->getAdvertising()->isAdvertising()) { - printf("Sleeping for %" PRIu32" seconds\n", sleepTime); + printf("Sleeping for %" PRIu32 " seconds\n", sleepTime); esp_deep_sleep_start(); } - }; -}; + } +} serverCallbacks; -/* Callback class to handle advertising events */ -class advCallbacks: public NimBLEExtAdvertisingCallbacks { - void onStopped(NimBLEExtAdvertising* pAdv, int reason, uint8_t inst_id) { +/** Callback class to handle advertising events */ +class AdvCallbacks : public NimBLEExtAdvertisingCallbacks { + void onStopped(NimBLEExtAdvertising* pAdv, int reason, uint8_t instId) override { /* 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) { case 0: printf(" client connecting\n"); return; case BLE_HS_ETIMEOUT: - printf("Time expired - sleeping for %" PRIu32" seconds\n", sleepTime); + printf("Time expired - sleeping for %" PRIu32 " seconds\n", sleepTime); break; default: break; @@ -72,90 +70,90 @@ class advCallbacks: public NimBLEExtAdvertisingCallbacks { bool m_updatedSR = false; - void onScanRequest(NimBLEExtAdvertising* pAdv, uint8_t inst_id, NimBLEAddress addr) { - printf("Scan request for instance %u\n", inst_id); + void onScanRequest(NimBLEExtAdvertising* pAdv, uint8_t instId, NimBLEAddress addr) override { + printf("Scan request for instance %u\n", instId); // if the data has already been updated we don't need to change it again. if (!m_updatedSR) { printf("Updating scan data\n"); NimBLEExtAdvertisement sr; sr.setServiceData(NimBLEUUID(SERVICE_UUID), std::string("Hello from scan response!")); - pAdv->setScanResponseData(inst_id, sr); + pAdv->setScanResponseData(instId, sr); 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"); - /* Create a server for our legacy advertiser */ - NimBLEServer *pServer = NimBLEDevice::createServer(); - pServer->setCallbacks(new ServerCallbacks); + /** Create a server for our legacy advertiser */ + NimBLEServer* pServer = NimBLEDevice::createServer(); + pServer->setCallbacks(&serverCallbacks); - NimBLEService *pService = pServer->createService(SERVICE_UUID); - NimBLECharacteristic *pCharacteristic = pService->createCharacteristic(CHARACTERISTIC_UUID, - NIMBLE_PROPERTY::READ | - NIMBLE_PROPERTY::WRITE | - NIMBLE_PROPERTY::NOTIFY); + NimBLEService* pService = pServer->createService(SERVICE_UUID); + NimBLECharacteristic* pCharacteristic = + pService->createCharacteristic(CHARACTERISTIC_UUID, + NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::NOTIFY); pCharacteristic->setValue("Hello World"); - /* Start the service */ + /** Start the service */ 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); - // Legacy advertising as a connectable device. + /** Legacy advertising as a connectable device. */ NimBLEExtAdvertisement legacyConnectable; - // Optional scan response data. + /** Optional scan response data. */ 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.setConnectable(false); - /* Set the initial data */ + /** Set the initial data */ 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); - /* Optional custom address for this advertisment. */ + /** Optional custom address for this advertisment. */ legacyConnectable.setAddress(NimBLEAddress("DE:AD:BE:EF:BA:AD")); - /* Set the advertising data. */ + /** Set the advertising data. */ legacyConnectable.setName("Legacy"); legacyConnectable.setCompleteServices16({NimBLEUUID(SERVICE_UUID)}); - /* Set the legacy and connectable flags. */ + /** Set the legacy and connectable flags. */ legacyConnectable.setLegacyAdvertising(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"); - /* Get the advertising ready */ + /** Get the advertising ready */ NimBLEExtAdvertising* pAdvertising = NimBLEDevice::getAdvertising(); - /* Set the callbacks to handle advertising events */ - pAdvertising->setCallbacks(new advCallbacks); + /** Set the callbacks to handle advertising events */ + 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. - * Note that the legacy scan response data needs to be set to the same 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). */ - if (pAdvertising->setInstanceData( 0, extScannable ) && - pAdvertising->setInstanceData( 1, legacyConnectable ) && - pAdvertising->setScanResponseData( 1, legacyScanResponse )) { - /* - * `NimBLEExtAdvertising::start` takes the advertisement instance ID to start - * and a duration in milliseconds or a max number of advertisements to send (or both). + if (pAdvertising->setInstanceData(0, extScannable) && pAdvertising->setInstanceData(1, legacyConnectable) && + pAdvertising->setScanResponseData(1, legacyScanResponse)) { + /** + * NimBLEExtAdvertising::start takes the advertisement instance ID to start + * and a duration in milliseconds or a max number of advertisements to send (or both). */ if (pAdvertising->start(0, advTime) && pAdvertising->start(1, advTime)) { printf("Started advertising\n"); @@ -163,7 +161,7 @@ void app_main (void) { printf("Failed to start advertising\n"); } } else { - printf("Failed to register advertisment data\n"); + printf("Failed to register advertisement data\n"); } esp_sleep_enable_timer_wakeup(sleepTime * 1000000); diff --git a/lib/libesp32_div/esp-nimble-cpp/examples/basic/BLE_scan/CMakeLists.txt b/lib/libesp32_div/esp-nimble-cpp/examples/Continuous_scan/CMakeLists.txt similarity index 81% rename from lib/libesp32_div/esp-nimble-cpp/examples/basic/BLE_scan/CMakeLists.txt rename to lib/libesp32_div/esp-nimble-cpp/examples/Continuous_scan/CMakeLists.txt index 0f64bee71..6a4f26320 100644 --- a/lib/libesp32_div/esp-nimble-cpp/examples/basic/BLE_scan/CMakeLists.txt +++ b/lib/libesp32_div/esp-nimble-cpp/examples/Continuous_scan/CMakeLists.txt @@ -3,5 +3,4 @@ cmake_minimum_required(VERSION 3.5) include($ENV{IDF_PATH}/tools/cmake/project.cmake) -set(SUPPORTED_TARGETS esp32) -project(BLE_scan) +project(Continuous_scan) diff --git a/lib/libesp32_div/esp-nimble-cpp/examples/Advanced/NimBLE_Client/main/CMakeLists.txt b/lib/libesp32_div/esp-nimble-cpp/examples/Continuous_scan/main/CMakeLists.txt similarity index 100% rename from lib/libesp32_div/esp-nimble-cpp/examples/Advanced/NimBLE_Client/main/CMakeLists.txt rename to lib/libesp32_div/esp-nimble-cpp/examples/Continuous_scan/main/CMakeLists.txt diff --git a/lib/libesp32_div/esp-nimble-cpp/examples/Continuous_scan/main/main.cpp b/lib/libesp32_div/esp-nimble-cpp/examples/Continuous_scan/main/main.cpp new file mode 100644 index 000000000..7287f26b9 --- /dev/null +++ b/lib/libesp32_div/esp-nimble-cpp/examples/Continuous_scan/main/main.cpp @@ -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 + +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"); +} \ No newline at end of file diff --git a/lib/libesp32_div/esp-nimble-cpp/examples/L2CAP/.gitignore b/lib/libesp32_div/esp-nimble-cpp/examples/L2CAP/.gitignore new file mode 100644 index 000000000..f9553772e --- /dev/null +++ b/lib/libesp32_div/esp-nimble-cpp/examples/L2CAP/.gitignore @@ -0,0 +1,5 @@ +.vscode +build +sdkconfig +sdkconfig.old +dependencies.lock diff --git a/lib/libesp32_div/esp-nimble-cpp/examples/NimBLE_server_get_client_name/CMakeLists.txt b/lib/libesp32_div/esp-nimble-cpp/examples/L2CAP/L2CAP_Client/CMakeLists.txt similarity index 74% rename from lib/libesp32_div/esp-nimble-cpp/examples/NimBLE_server_get_client_name/CMakeLists.txt rename to lib/libesp32_div/esp-nimble-cpp/examples/L2CAP/L2CAP_Client/CMakeLists.txt index 21c12dad3..57b688295 100644 --- a/lib/libesp32_div/esp-nimble-cpp/examples/NimBLE_server_get_client_name/CMakeLists.txt +++ b/lib/libesp32_div/esp-nimble-cpp/examples/L2CAP/L2CAP_Client/CMakeLists.txt @@ -3,5 +3,5 @@ cmake_minimum_required(VERSION 3.5) include($ENV{IDF_PATH}/tools/cmake/project.cmake) -set(SUPPORTED_TARGETS esp32) -project(NimBLE_server_get_client_name) +set(SUPPORTED_TARGETS esp32 esp32s3 esp32c3 esp32c6) +project(L2CAP_client) diff --git a/lib/libesp32_div/esp-nimble-cpp/examples/basic/BLE_scan/Makefile b/lib/libesp32_div/esp-nimble-cpp/examples/L2CAP/L2CAP_Client/Makefile similarity index 56% rename from lib/libesp32_div/esp-nimble-cpp/examples/basic/BLE_scan/Makefile rename to lib/libesp32_div/esp-nimble-cpp/examples/L2CAP/L2CAP_Client/Makefile index ebca2769d..7eeac87b6 100644 --- a/lib/libesp32_div/esp-nimble-cpp/examples/basic/BLE_scan/Makefile +++ b/lib/libesp32_div/esp-nimble-cpp/examples/L2CAP/L2CAP_Client/Makefile @@ -1,3 +1,3 @@ -PROJECT_NAME := BLE_scan +PROJECT_NAME := L2CAP_client include $(IDF_PATH)/make/project.mk diff --git a/lib/libesp32_div/esp-nimble-cpp/examples/Advanced/NimBLE_Server/main/CMakeLists.txt b/lib/libesp32_div/esp-nimble-cpp/examples/L2CAP/L2CAP_Client/main/CMakeLists.txt similarity index 100% rename from lib/libesp32_div/esp-nimble-cpp/examples/Advanced/NimBLE_Server/main/CMakeLists.txt rename to lib/libesp32_div/esp-nimble-cpp/examples/L2CAP/L2CAP_Client/main/CMakeLists.txt diff --git a/lib/libesp32_div/esp-nimble-cpp/examples/Advanced/NimBLE_Client/main/component.mk b/lib/libesp32_div/esp-nimble-cpp/examples/L2CAP/L2CAP_Client/main/component.mk similarity index 100% rename from lib/libesp32_div/esp-nimble-cpp/examples/Advanced/NimBLE_Client/main/component.mk rename to lib/libesp32_div/esp-nimble-cpp/examples/L2CAP/L2CAP_Client/main/component.mk diff --git a/lib/libesp32_div/esp-nimble-cpp/examples/L2CAP/L2CAP_Client/main/idf_component.yml b/lib/libesp32_div/esp-nimble-cpp/examples/L2CAP/L2CAP_Client/main/idf_component.yml new file mode 100644 index 000000000..66f47cad6 --- /dev/null +++ b/lib/libesp32_div/esp-nimble-cpp/examples/L2CAP/L2CAP_Client/main/idf_component.yml @@ -0,0 +1,3 @@ +dependencies: + local/esp-nimble-cpp: + path: ../../../../../esp-nimble-cpp/ diff --git a/lib/libesp32_div/esp-nimble-cpp/examples/L2CAP/L2CAP_Client/main/main.cpp b/lib/libesp32_div/esp-nimble-cpp/examples/L2CAP/L2CAP_Client/main/main.cpp new file mode 100644 index 000000000..e4f21913d --- /dev/null +++ b/lib/libesp32_div/esp-nimble-cpp/examples/L2CAP/L2CAP_Client/main/main.cpp @@ -0,0 +1,165 @@ +#include + +// 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& 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 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); + } +} diff --git a/lib/libesp32_div/esp-nimble-cpp/examples/L2CAP/L2CAP_Server/CMakeLists.txt b/lib/libesp32_div/esp-nimble-cpp/examples/L2CAP/L2CAP_Server/CMakeLists.txt new file mode 100644 index 000000000..ba68eccc7 --- /dev/null +++ b/lib/libesp32_div/esp-nimble-cpp/examples/L2CAP/L2CAP_Server/CMakeLists.txt @@ -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) diff --git a/lib/libesp32_div/esp-nimble-cpp/examples/basic/BLE_server/Makefile b/lib/libesp32_div/esp-nimble-cpp/examples/L2CAP/L2CAP_Server/Makefile similarity index 56% rename from lib/libesp32_div/esp-nimble-cpp/examples/basic/BLE_server/Makefile rename to lib/libesp32_div/esp-nimble-cpp/examples/L2CAP/L2CAP_Server/Makefile index 92dd6cdb2..f889e7e05 100644 --- a/lib/libesp32_div/esp-nimble-cpp/examples/basic/BLE_server/Makefile +++ b/lib/libesp32_div/esp-nimble-cpp/examples/L2CAP/L2CAP_Server/Makefile @@ -1,3 +1,3 @@ -PROJECT_NAME := BLE_server +PROJECT_NAME := L2CAP_server include $(IDF_PATH)/make/project.mk diff --git a/lib/libesp32_div/esp-nimble-cpp/examples/basic/BLE_client/main/CMakeLists.txt b/lib/libesp32_div/esp-nimble-cpp/examples/L2CAP/L2CAP_Server/main/CMakeLists.txt similarity index 100% rename from lib/libesp32_div/esp-nimble-cpp/examples/basic/BLE_client/main/CMakeLists.txt rename to lib/libesp32_div/esp-nimble-cpp/examples/L2CAP/L2CAP_Server/main/CMakeLists.txt diff --git a/lib/libesp32_div/esp-nimble-cpp/examples/Advanced/NimBLE_Server/main/component.mk b/lib/libesp32_div/esp-nimble-cpp/examples/L2CAP/L2CAP_Server/main/component.mk similarity index 100% rename from lib/libesp32_div/esp-nimble-cpp/examples/Advanced/NimBLE_Server/main/component.mk rename to lib/libesp32_div/esp-nimble-cpp/examples/L2CAP/L2CAP_Server/main/component.mk diff --git a/lib/libesp32_div/esp-nimble-cpp/examples/L2CAP/L2CAP_Server/main/idf_component.yml b/lib/libesp32_div/esp-nimble-cpp/examples/L2CAP/L2CAP_Server/main/idf_component.yml new file mode 100644 index 000000000..66f47cad6 --- /dev/null +++ b/lib/libesp32_div/esp-nimble-cpp/examples/L2CAP/L2CAP_Server/main/idf_component.yml @@ -0,0 +1,3 @@ +dependencies: + local/esp-nimble-cpp: + path: ../../../../../esp-nimble-cpp/ diff --git a/lib/libesp32_div/esp-nimble-cpp/examples/L2CAP/L2CAP_Server/main/main.cpp b/lib/libesp32_div/esp-nimble-cpp/examples/L2CAP/L2CAP_Server/main/main.cpp new file mode 100644 index 000000000..476390721 --- /dev/null +++ b/lib/libesp32_div/esp-nimble-cpp/examples/L2CAP/L2CAP_Server/main/main.cpp @@ -0,0 +1,90 @@ +#include + +// 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& 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()); + } +} diff --git a/lib/libesp32_div/esp-nimble-cpp/examples/basic/BLE_client/CMakeLists.txt b/lib/libesp32_div/esp-nimble-cpp/examples/NimBLE_Async_Client/CMakeLists.txt similarity index 81% rename from lib/libesp32_div/esp-nimble-cpp/examples/basic/BLE_client/CMakeLists.txt rename to lib/libesp32_div/esp-nimble-cpp/examples/NimBLE_Async_Client/CMakeLists.txt index 8f619c4ed..dcfcd4b99 100644 --- a/lib/libesp32_div/esp-nimble-cpp/examples/basic/BLE_client/CMakeLists.txt +++ b/lib/libesp32_div/esp-nimble-cpp/examples/NimBLE_Async_Client/CMakeLists.txt @@ -3,5 +3,4 @@ cmake_minimum_required(VERSION 3.5) include($ENV{IDF_PATH}/tools/cmake/project.cmake) -set(SUPPORTED_TARGETS esp32) -project(BLE_client) +project(NimBLE_Async_Client) diff --git a/lib/libesp32_div/esp-nimble-cpp/examples/basic/BLE_notify/main/CMakeLists.txt b/lib/libesp32_div/esp-nimble-cpp/examples/NimBLE_Async_Client/main/CMakeLists.txt similarity index 100% rename from lib/libesp32_div/esp-nimble-cpp/examples/basic/BLE_notify/main/CMakeLists.txt rename to lib/libesp32_div/esp-nimble-cpp/examples/NimBLE_Async_Client/main/CMakeLists.txt diff --git a/lib/libesp32_div/esp-nimble-cpp/examples/NimBLE_Async_Client/main/main.cpp b/lib/libesp32_div/esp-nimble-cpp/examples/NimBLE_Async_Client/main/main.cpp new file mode 100644 index 000000000..8a055a1de --- /dev/null +++ b/lib/libesp32_div/esp-nimble-cpp/examples/NimBLE_Async_Client/main/main.cpp @@ -0,0 +1,83 @@ + +/** + * NimBLE_Async_client Demo: + * + * Demonstrates asynchronous client operations. + * + * Created: on November 4, 2024 + * Author: H2zero + */ + +#include + +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); + } +} diff --git a/lib/libesp32_div/esp-nimble-cpp/examples/Advanced/NimBLE_Client/CMakeLists.txt b/lib/libesp32_div/esp-nimble-cpp/examples/NimBLE_Client/CMakeLists.txt similarity index 89% rename from lib/libesp32_div/esp-nimble-cpp/examples/Advanced/NimBLE_Client/CMakeLists.txt rename to lib/libesp32_div/esp-nimble-cpp/examples/NimBLE_Client/CMakeLists.txt index 7b68bed12..5da644a31 100644 --- a/lib/libesp32_div/esp-nimble-cpp/examples/Advanced/NimBLE_Client/CMakeLists.txt +++ b/lib/libesp32_div/esp-nimble-cpp/examples/NimBLE_Client/CMakeLists.txt @@ -3,5 +3,4 @@ cmake_minimum_required(VERSION 3.5) include($ENV{IDF_PATH}/tools/cmake/project.cmake) -set(SUPPORTED_TARGETS esp32) project(NimBLE_Client) diff --git a/lib/libesp32_div/esp-nimble-cpp/examples/basic/BLE_scan/main/CMakeLists.txt b/lib/libesp32_div/esp-nimble-cpp/examples/NimBLE_Client/main/CMakeLists.txt similarity index 100% rename from lib/libesp32_div/esp-nimble-cpp/examples/basic/BLE_scan/main/CMakeLists.txt rename to lib/libesp32_div/esp-nimble-cpp/examples/NimBLE_Client/main/CMakeLists.txt diff --git a/lib/libesp32_div/esp-nimble-cpp/examples/NimBLE_Client/main/main.cpp b/lib/libesp32_div/esp-nimble-cpp/examples/NimBLE_Client/main/main.cpp new file mode 100644 index 000000000..ff9acbfef --- /dev/null +++ b/lib/libesp32_div/esp-nimble-cpp/examples/NimBLE_Client/main/main.cpp @@ -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 + +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); + } + } +} diff --git a/lib/libesp32_div/esp-nimble-cpp/examples/Advanced/NimBLE_Server/CMakeLists.txt b/lib/libesp32_div/esp-nimble-cpp/examples/NimBLE_Server/CMakeLists.txt similarity index 89% rename from lib/libesp32_div/esp-nimble-cpp/examples/Advanced/NimBLE_Server/CMakeLists.txt rename to lib/libesp32_div/esp-nimble-cpp/examples/NimBLE_Server/CMakeLists.txt index e24a91bb4..8cad49fc2 100644 --- a/lib/libesp32_div/esp-nimble-cpp/examples/Advanced/NimBLE_Server/CMakeLists.txt +++ b/lib/libesp32_div/esp-nimble-cpp/examples/NimBLE_Server/CMakeLists.txt @@ -3,5 +3,4 @@ cmake_minimum_required(VERSION 3.5) include($ENV{IDF_PATH}/tools/cmake/project.cmake) -set(SUPPORTED_TARGETS esp32) project(NimBLE_Server) diff --git a/lib/libesp32_div/esp-nimble-cpp/examples/basic/BLE_server/main/CMakeLists.txt b/lib/libesp32_div/esp-nimble-cpp/examples/NimBLE_Server/main/CMakeLists.txt similarity index 100% rename from lib/libesp32_div/esp-nimble-cpp/examples/basic/BLE_server/main/CMakeLists.txt rename to lib/libesp32_div/esp-nimble-cpp/examples/NimBLE_Server/main/CMakeLists.txt diff --git a/lib/libesp32_div/esp-nimble-cpp/examples/Advanced/NimBLE_Server/main/main.cpp b/lib/libesp32_div/esp-nimble-cpp/examples/NimBLE_Server/main/main.cpp similarity index 50% rename from lib/libesp32_div/esp-nimble-cpp/examples/Advanced/NimBLE_Server/main/main.cpp rename to lib/libesp32_div/esp-nimble-cpp/examples/NimBLE_Server/main/main.cpp index 3effb54f2..7d7257dfc 100644 --- a/lib/libesp32_div/esp-nimble-cpp/examples/Advanced/NimBLE_Server/main/main.cpp +++ b/lib/libesp32_div/esp-nimble-cpp/examples/NimBLE_Server/main/main.cpp @@ -1,224 +1,186 @@ -/** NimBLE_Server Demo: +/** + * NimBLE_Server Demo: * * Demonstrates many of the available features of the NimBLE server library. * * Created: on March 22 2020 * Author: H2zero - * -*/ -#include "NimBLEDevice.h" -#include "NimBLELog.h" + */ -#include - -extern "C" {void app_main(void);} +#include static NimBLEServer* pServer; /** None of these are required as they will be handled by the library with defaults. ** ** Remove as you see fit for your needs */ -class ServerCallbacks: public NimBLEServerCallbacks { - void onConnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo) { +class ServerCallbacks : public NimBLEServerCallbacks { + void onConnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo) override { 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 * latency, supervision timeout. * Units; Min/Max Intervals: 1.25 millisecond increments. * 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"); 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()); - pServer->updateConnParams(connInfo.getConnHandle(), 24, 48, 0, 60); - }; + } -/********************* Security handled here ********************** -****** Note: these are the same return values as defaults ********/ - uint32_t onPassKeyDisplay(){ + /********************* Security handled here *********************/ + uint32_t onPassKeyDisplay() override { 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. */ 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); /** 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 */ - if(!connInfo.isEncrypted()) { + if (!connInfo.isEncrypted()) { NimBLEDevice::getServer()->disconnect(connInfo.getConnHandle()); printf("Encrypt connection failed - disconnecting client\n"); return; } - printf("Starting BLE work!"); - }; -}; + + printf("Secured connection to: %s\n", connInfo.getAddress().toString().c_str()); + } +} serverCallbacks; /** Handler class for characteristic actions */ -class CharacteristicCallbacks: public NimBLECharacteristicCallbacks { - void onRead(NimBLECharacteristic* pCharacteristic, NimBLEConnInfo& connInfo) { +class CharacteristicCallbacks : public NimBLECharacteristicCallbacks { + void onRead(NimBLECharacteristic* pCharacteristic, NimBLEConnInfo& connInfo) override { printf("%s : onRead(), value: %s\n", pCharacteristic->getUUID().toString().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", pCharacteristic->getUUID().toString().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. */ - void onStatus(NimBLECharacteristic* pCharacteristic, int code) { - printf("Notification/Indication return code: %d, %s\n", - code, NimBLEUtils::returnCodeToString(code)); + void onStatus(NimBLECharacteristic* pCharacteristic, int code) override { + printf("Notification/Indication return code: %d, %s\n", code, NimBLEUtils::returnCodeToString(code)); } - void onSubscribe(NimBLECharacteristic* pCharacteristic, NimBLEConnInfo& connInfo, uint16_t subValue) { - std::string str = "Client ID: "; - str += connInfo.getConnHandle(); - str += " Address: "; - str += connInfo.getAddress().toString(); - if(subValue == 0) { + /** Peer subscribed to notifications/indications */ + void onSubscribe(NimBLECharacteristic* pCharacteristic, NimBLEConnInfo& connInfo, uint16_t subValue) override { + std::string str = "Client ID: "; + str += connInfo.getConnHandle(); + str += " Address: "; + str += connInfo.getAddress().toString(); + if (subValue == 0) { str += " Unsubscribed to "; - }else if(subValue == 1) { - str += " Subscribed to notfications for "; - } else if(subValue == 2) { + } else if (subValue == 1) { + str += " Subscribed to notifications for "; + } else if (subValue == 2) { str += " Subscribed to indications for "; - } else if(subValue == 3) { + } else if (subValue == 3) { str += " Subscribed to notifications and indications for "; } str += std::string(pCharacteristic->getUUID()); printf("%s\n", str.c_str()); } -}; +} chrCallbacks; /** Handler class for descriptor actions */ class DescriptorCallbacks : public NimBLEDescriptorCallbacks { - void onWrite(NimBLEDescriptor* pDescriptor, NimBLEConnInfo& connInfo) { + void onWrite(NimBLEDescriptor* pDescriptor, NimBLEConnInfo& connInfo) override { std::string dscVal = pDescriptor->getValue(); - printf("Descriptor witten 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); + printf("Descriptor written value: %s\n", dscVal.c_str()); } - 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"); - /** sets device name */ + /** Initialize NimBLE and set the device name */ 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_YESNO - Numeric comparison 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_YESNO); //use numeric comparison + // NimBLEDevice::setSecurityIOCap(BLE_HS_IO_DISPLAY_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. + /** + * 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(false, false, true); + NimBLEDevice::setSecurityAuth(/*BLE_SM_PAIR_AUTHREQ_BOND | BLE_SM_PAIR_AUTHREQ_MITM |*/ BLE_SM_PAIR_AUTHREQ_SC); - pServer = NimBLEDevice::createServer(); - pServer->setCallbacks(new ServerCallbacks()); + pServer->setCallbacks(&serverCallbacks); - NimBLEService* pDeadService = pServer->createService("DEAD"); - NimBLECharacteristic* pBeefCharacteristic = pDeadService->createCharacteristic( - "BEEF", - NIMBLE_PROPERTY::READ | - NIMBLE_PROPERTY::WRITE | - /** Require a secure connection for read and write access */ - NIMBLE_PROPERTY::READ_ENC | // only allow reading if paired / encrypted - NIMBLE_PROPERTY::WRITE_ENC // only allow writing if paired / encrypted - ); + NimBLEService* pDeadService = pServer->createService("DEAD"); + NimBLECharacteristic* pBeefCharacteristic = + pDeadService->createCharacteristic("BEEF", + NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | + /** Require a secure connection for read and write access */ + NIMBLE_PROPERTY::READ_ENC | // only allow reading if paired / encrypted + NIMBLE_PROPERTY::WRITE_ENC // only allow writing if paired / encrypted + ); pBeefCharacteristic->setValue("Burger"); 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 * and sizes. However we must cast the returned reference to the correct type as the method * only returns a pointer to the base NimBLEDescriptor class. */ - NimBLE2904* pBeef2904 = (NimBLE2904*)pBeefCharacteristic->createDescriptor("2904"); + NimBLE2904* pBeef2904 = pBeefCharacteristic->create2904(); pBeef2904->setFormat(NimBLE2904::FORMAT_UTF8); pBeef2904->setCallbacks(&dscCallbacks); - - NimBLEService* pBaadService = pServer->createService("BAAD"); - NimBLECharacteristic* pFoodCharacteristic = pBaadService->createCharacteristic( - "F00D", - NIMBLE_PROPERTY::READ | - NIMBLE_PROPERTY::WRITE | - NIMBLE_PROPERTY::NOTIFY - ); + NimBLEService* pBaadService = pServer->createService("BAAD"); + NimBLECharacteristic* pFoodCharacteristic = + pBaadService->createCharacteristic("F00D", NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::NOTIFY); pFoodCharacteristic->setValue("Fries"); pFoodCharacteristic->setCallbacks(&chrCallbacks); - /** Custom descriptor: Arguments are UUID, Properties, max length in bytes of the value */ - NimBLEDescriptor* pC01Ddsc = pFoodCharacteristic->createDescriptor( - "C01D", - NIMBLE_PROPERTY::READ | - NIMBLE_PROPERTY::WRITE| - NIMBLE_PROPERTY::WRITE_ENC, // only allow writing if paired / encrypted - 20 - ); + /** Custom descriptor: Arguments are UUID, Properties, max length of the value in bytes */ + NimBLEDescriptor* pC01Ddsc = + pFoodCharacteristic->createDescriptor("C01D", + NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::WRITE_ENC, + 20); pC01Ddsc->setValue("Send it back!"); pC01Ddsc->setCallbacks(&dscCallbacks); @@ -226,17 +188,31 @@ void app_main(void) { pDeadService->start(); pBaadService->start(); + /** Create an advertising instance and add the services to the advertised data */ NimBLEAdvertising* pAdvertising = NimBLEDevice::getAdvertising(); - /** Add the services to the advertisment data **/ + pAdvertising->setName("NimBLE-Server"); pAdvertising->addServiceUUID(pDeadService->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. */ - pAdvertising->setScanResponse(true); + pAdvertising->enableScanResponse(true); pAdvertising->start(); 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); + } } diff --git a/lib/libesp32_div/esp-nimble-cpp/examples/NimBLE_active_passive_scan/NimBLE_active_passive_scan.ino b/lib/libesp32_div/esp-nimble-cpp/examples/NimBLE_active_passive_scan/NimBLE_active_passive_scan.ino deleted file mode 100644 index e69de29bb..000000000 diff --git a/lib/libesp32_div/esp-nimble-cpp/examples/NimBLE_server_get_client_name/main/main.cpp b/lib/libesp32_div/esp-nimble-cpp/examples/NimBLE_server_get_client_name/main/main.cpp deleted file mode 100644 index e255807f8..000000000 --- a/lib/libesp32_div/esp-nimble-cpp/examples/NimBLE_server_get_client_name/main/main.cpp +++ /dev/null @@ -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 - -// 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)); - } -} diff --git a/lib/libesp32_div/esp-nimble-cpp/examples/basic/BLE_client/Makefile b/lib/libesp32_div/esp-nimble-cpp/examples/basic/BLE_client/Makefile deleted file mode 100644 index d2e7b5af8..000000000 --- a/lib/libesp32_div/esp-nimble-cpp/examples/basic/BLE_client/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -PROJECT_NAME := BLE_client - -include $(IDF_PATH)/make/project.mk diff --git a/lib/libesp32_div/esp-nimble-cpp/examples/basic/BLE_client/main/component.mk b/lib/libesp32_div/esp-nimble-cpp/examples/basic/BLE_client/main/component.mk deleted file mode 100644 index a98f634ea..000000000 --- a/lib/libesp32_div/esp-nimble-cpp/examples/basic/BLE_client/main/component.mk +++ /dev/null @@ -1,4 +0,0 @@ -# -# "main" pseudo-component makefile. -# -# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) diff --git a/lib/libesp32_div/esp-nimble-cpp/examples/basic/BLE_client/main/main.cpp b/lib/libesp32_div/esp-nimble-cpp/examples/basic/BLE_client/main/main.cpp deleted file mode 100644 index cfb80e9bf..000000000 --- a/lib/libesp32_div/esp-nimble-cpp/examples/basic/BLE_client/main/main.cpp +++ /dev/null @@ -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. - diff --git a/lib/libesp32_div/esp-nimble-cpp/examples/basic/BLE_client/sdkconfig.defaults b/lib/libesp32_div/esp-nimble-cpp/examples/basic/BLE_client/sdkconfig.defaults deleted file mode 100644 index c829fc5c0..000000000 --- a/lib/libesp32_div/esp-nimble-cpp/examples/basic/BLE_client/sdkconfig.defaults +++ /dev/null @@ -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 diff --git a/lib/libesp32_div/esp-nimble-cpp/examples/basic/BLE_notify/CMakeLists.txt b/lib/libesp32_div/esp-nimble-cpp/examples/basic/BLE_notify/CMakeLists.txt deleted file mode 100644 index dbfacf5be..000000000 --- a/lib/libesp32_div/esp-nimble-cpp/examples/basic/BLE_notify/CMakeLists.txt +++ /dev/null @@ -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) diff --git a/lib/libesp32_div/esp-nimble-cpp/examples/basic/BLE_notify/Makefile b/lib/libesp32_div/esp-nimble-cpp/examples/basic/BLE_notify/Makefile deleted file mode 100644 index b895d997a..000000000 --- a/lib/libesp32_div/esp-nimble-cpp/examples/basic/BLE_notify/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -PROJECT_NAME := BLE_notify - -include $(IDF_PATH)/make/project.mk diff --git a/lib/libesp32_div/esp-nimble-cpp/examples/basic/BLE_notify/main/component.mk b/lib/libesp32_div/esp-nimble-cpp/examples/basic/BLE_notify/main/component.mk deleted file mode 100644 index a98f634ea..000000000 --- a/lib/libesp32_div/esp-nimble-cpp/examples/basic/BLE_notify/main/component.mk +++ /dev/null @@ -1,4 +0,0 @@ -# -# "main" pseudo-component makefile. -# -# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) diff --git a/lib/libesp32_div/esp-nimble-cpp/examples/basic/BLE_notify/main/main.cpp b/lib/libesp32_div/esp-nimble-cpp/examples/basic/BLE_notify/main/main.cpp deleted file mode 100644 index b17f49aaa..000000000 --- a/lib/libesp32_div/esp-nimble-cpp/examples/basic/BLE_notify/main/main.cpp +++ /dev/null @@ -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 -#include -#include -#include -***********************/ -#include - -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"); -} diff --git a/lib/libesp32_div/esp-nimble-cpp/examples/basic/BLE_notify/sdkconfig.defaults b/lib/libesp32_div/esp-nimble-cpp/examples/basic/BLE_notify/sdkconfig.defaults deleted file mode 100644 index c829fc5c0..000000000 --- a/lib/libesp32_div/esp-nimble-cpp/examples/basic/BLE_notify/sdkconfig.defaults +++ /dev/null @@ -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 diff --git a/lib/libesp32_div/esp-nimble-cpp/examples/basic/BLE_scan/main/component.mk b/lib/libesp32_div/esp-nimble-cpp/examples/basic/BLE_scan/main/component.mk deleted file mode 100644 index a98f634ea..000000000 --- a/lib/libesp32_div/esp-nimble-cpp/examples/basic/BLE_scan/main/component.mk +++ /dev/null @@ -1,4 +0,0 @@ -# -# "main" pseudo-component makefile. -# -# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) diff --git a/lib/libesp32_div/esp-nimble-cpp/examples/basic/BLE_scan/main/main.cpp b/lib/libesp32_div/esp-nimble-cpp/examples/basic/BLE_scan/main/main.cpp deleted file mode 100644 index d936c0104..000000000 --- a/lib/libesp32_div/esp-nimble-cpp/examples/basic/BLE_scan/main/main.cpp +++ /dev/null @@ -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 -#include -#include -#include -***********************/ - -#include - -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); -} diff --git a/lib/libesp32_div/esp-nimble-cpp/examples/basic/BLE_scan/sdkconfig.defaults b/lib/libesp32_div/esp-nimble-cpp/examples/basic/BLE_scan/sdkconfig.defaults deleted file mode 100644 index c829fc5c0..000000000 --- a/lib/libesp32_div/esp-nimble-cpp/examples/basic/BLE_scan/sdkconfig.defaults +++ /dev/null @@ -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 diff --git a/lib/libesp32_div/esp-nimble-cpp/examples/basic/BLE_server/CMakeLists.txt b/lib/libesp32_div/esp-nimble-cpp/examples/basic/BLE_server/CMakeLists.txt deleted file mode 100644 index 03b5365a5..000000000 --- a/lib/libesp32_div/esp-nimble-cpp/examples/basic/BLE_server/CMakeLists.txt +++ /dev/null @@ -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) diff --git a/lib/libesp32_div/esp-nimble-cpp/examples/basic/BLE_server/main/component.mk b/lib/libesp32_div/esp-nimble-cpp/examples/basic/BLE_server/main/component.mk deleted file mode 100644 index a98f634ea..000000000 --- a/lib/libesp32_div/esp-nimble-cpp/examples/basic/BLE_server/main/component.mk +++ /dev/null @@ -1,4 +0,0 @@ -# -# "main" pseudo-component makefile. -# -# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) diff --git a/lib/libesp32_div/esp-nimble-cpp/examples/basic/BLE_server/main/main.cpp b/lib/libesp32_div/esp-nimble-cpp/examples/basic/BLE_server/main/main.cpp deleted file mode 100644 index 85c0a3ede..000000000 --- a/lib/libesp32_div/esp-nimble-cpp/examples/basic/BLE_server/main/main.cpp +++ /dev/null @@ -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 -#include -#include -***********************/ - -#include - -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"); -} diff --git a/lib/libesp32_div/esp-nimble-cpp/examples/basic/BLE_server/sdkconfig.defaults b/lib/libesp32_div/esp-nimble-cpp/examples/basic/BLE_server/sdkconfig.defaults deleted file mode 100644 index c829fc5c0..000000000 --- a/lib/libesp32_div/esp-nimble-cpp/examples/basic/BLE_server/sdkconfig.defaults +++ /dev/null @@ -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 diff --git a/lib/libesp32_div/esp-nimble-cpp/examples/basic/BLE_uart/Makefile b/lib/libesp32_div/esp-nimble-cpp/examples/basic/BLE_uart/Makefile deleted file mode 100644 index 9323b5758..000000000 --- a/lib/libesp32_div/esp-nimble-cpp/examples/basic/BLE_uart/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -PROJECT_NAME := BLE_uart - -include $(IDF_PATH)/make/project.mk diff --git a/lib/libesp32_div/esp-nimble-cpp/examples/basic/BLE_uart/main/CMakeLists.txt b/lib/libesp32_div/esp-nimble-cpp/examples/basic/BLE_uart/main/CMakeLists.txt deleted file mode 100644 index 9be907511..000000000 --- a/lib/libesp32_div/esp-nimble-cpp/examples/basic/BLE_uart/main/CMakeLists.txt +++ /dev/null @@ -1,4 +0,0 @@ -set(COMPONENT_SRCS "main.cpp") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() \ No newline at end of file diff --git a/lib/libesp32_div/esp-nimble-cpp/examples/basic/BLE_uart/main/component.mk b/lib/libesp32_div/esp-nimble-cpp/examples/basic/BLE_uart/main/component.mk deleted file mode 100644 index a98f634ea..000000000 --- a/lib/libesp32_div/esp-nimble-cpp/examples/basic/BLE_uart/main/component.mk +++ /dev/null @@ -1,4 +0,0 @@ -# -# "main" pseudo-component makefile. -# -# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) diff --git a/lib/libesp32_div/esp-nimble-cpp/examples/basic/BLE_uart/main/main.cpp b/lib/libesp32_div/esp-nimble-cpp/examples/basic/BLE_uart/main/main.cpp deleted file mode 100644 index 18df6fa63..000000000 --- a/lib/libesp32_div/esp-nimble-cpp/examples/basic/BLE_uart/main/main.cpp +++ /dev/null @@ -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 -#include -#include -#include -***********************/ -#include - -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"); -} diff --git a/lib/libesp32_div/esp-nimble-cpp/examples/basic/BLE_uart/sdkconfig.defaults b/lib/libesp32_div/esp-nimble-cpp/examples/basic/BLE_uart/sdkconfig.defaults deleted file mode 100644 index c829fc5c0..000000000 --- a/lib/libesp32_div/esp-nimble-cpp/examples/basic/BLE_uart/sdkconfig.defaults +++ /dev/null @@ -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 diff --git a/lib/libesp32_div/esp-nimble-cpp/idf_component.yml b/lib/libesp32_div/esp-nimble-cpp/idf_component.yml new file mode 100644 index 000000000..55f816eed --- /dev/null +++ b/lib/libesp32_div/esp-nimble-cpp/idf_component.yml @@ -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 +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]" diff --git a/lib/libesp32_div/esp-nimble-cpp/library.json b/lib/libesp32_div/esp-nimble-cpp/library.json index a3d9c9ece..ae2aff345 100644 --- a/lib/libesp32_div/esp-nimble-cpp/library.json +++ b/lib/libesp32_div/esp-nimble-cpp/library.json @@ -1,8 +1,23 @@ { - "name": "esp-nimble-cpp", - "keywords": "esp32, bluetooth", - "description": "Bluetooth low energy (BLE) library for esp32 based on NimBLE", - "version": "1.4.1", - "frameworks": "arduino", - "platforms": "espressif32" + "name": "esp-nimble-cpp", + "version": "2.2.1", + "description": "C++ wrapper for the NimBLE BLE stack", + "keywords": [ + "BLE", + "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 + } } diff --git a/lib/libesp32_div/esp-nimble-cpp/library.properties b/lib/libesp32_div/esp-nimble-cpp/library.properties deleted file mode 100644 index dc0d52c1f..000000000 --- a/lib/libesp32_div/esp-nimble-cpp/library.properties +++ /dev/null @@ -1,10 +0,0 @@ -name=esp-nimble-cpp -version=1.4.1 -author=h2zero -maintainer=h2zero -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 \ No newline at end of file diff --git a/lib/libesp32_div/esp-nimble-cpp/package.json b/lib/libesp32_div/esp-nimble-cpp/package.json deleted file mode 100644 index 1efe13e63..000000000 --- a/lib/libesp32_div/esp-nimble-cpp/package.json +++ /dev/null @@ -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" - } -} diff --git a/lib/libesp32_div/esp-nimble-cpp/src/HIDTypes.h b/lib/libesp32_div/esp-nimble-cpp/src/HIDTypes.h index 8ee31dae6..4cdc919ec 100644 --- a/lib/libesp32_div/esp-nimble-cpp/src/HIDTypes.h +++ b/lib/libesp32_div/esp-nimble-cpp/src/HIDTypes.h @@ -25,9 +25,9 @@ #define HID_VERSION_1_11 (0x0111) /* HID Class */ -#define HID_CLASS (3) -#define HID_SUBCLASS_NONE (0) -#define HID_PROTOCOL_NONE (0) +#define BLE_HID_CLASS (3) +#define BLE_HID_SUBCLASS_NONE (0) +#define BLE_HID_PROTOCOL_NONE (0) /* Descriptors */ #define HID_DESCRIPTOR (33) diff --git a/lib/libesp32_div/esp-nimble-cpp/src/NimBLE2904.cpp b/lib/libesp32_div/esp-nimble-cpp/src/NimBLE2904.cpp index 486fa5ef5..240751395 100644 --- a/lib/libesp32_div/esp-nimble-cpp/src/NimBLE2904.cpp +++ b/lib/libesp32_div/esp-nimble-cpp/src/NimBLE2904.cpp @@ -1,82 +1,72 @@ /* - * NimBLE2904.cpp + * Copyright 2020-2025 Ryan Powell and + * esp-nimble-cpp, NimBLE-Arduino contributors. * - * Created: on March 13, 2020 - * Author H2zero + * 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 * - * Originally: + * http://www.apache.org/licenses/LICENSE-2.0 * - * BLE2904.cpp - * - * Created on: Dec 23, 2017 - * Author: kolban + * 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 "nimconfig.h" -#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) - #include "NimBLE2904.h" +#if CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL - -NimBLE2904::NimBLE2904(NimBLECharacteristic* pCharacteristic) -: NimBLEDescriptor(NimBLEUUID((uint16_t) 0x2904), - BLE_GATT_CHR_F_READ, - 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 - +NimBLE2904::NimBLE2904(NimBLECharacteristic* pChr) + : NimBLEDescriptor(NimBLEUUID((uint16_t)0x2904), BLE_GATT_CHR_F_READ, sizeof(NimBLE2904Data), pChr) { + setValue(m_data); +} // NimBLE2904 /** * @brief Set the description. + * @param [in] description The description value to set. */ void NimBLE2904::setDescription(uint16_t description) { m_data.m_description = description; - setValue((uint8_t*) &m_data, sizeof(m_data)); -} - + setValue(m_data); +} // setDescription /** * @brief Set the exponent. + * @param [in] exponent The exponent value to set. */ void NimBLE2904::setExponent(int8_t exponent) { m_data.m_exponent = exponent; - setValue((uint8_t*) &m_data, sizeof(m_data)); + setValue(m_data); } // setExponent - /** * @brief Set the format. + * @param [in] format The format value to set. */ void NimBLE2904::setFormat(uint8_t format) { m_data.m_format = format; - setValue((uint8_t*) &m_data, sizeof(m_data)); + setValue(m_data); } // setFormat - /** * @brief Set the namespace. + * @param [in] namespace_value The namespace value toset. */ void NimBLE2904::setNamespace(uint8_t namespace_value) { m_data.m_namespace = namespace_value; - setValue((uint8_t*) &m_data, sizeof(m_data)); + setValue(m_data); } // setNamespace - /** - * @brief Set the units for this value. It should be one of the encoded values defined here: - * https://www.bluetooth.com/specifications/assigned-numbers/units + * @brief Set the units for this value. * @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) { m_data.m_unit = unit; - setValue((uint8_t*) &m_data, sizeof(m_data)); + setValue(m_data); } // setUnit -#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL */ +#endif // CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL diff --git a/lib/libesp32_div/esp-nimble-cpp/src/NimBLE2904.h b/lib/libesp32_div/esp-nimble-cpp/src/NimBLE2904.h index 52ae2d3db..1c10e3b5a 100644 --- a/lib/libesp32_div/esp-nimble-cpp/src/NimBLE2904.h +++ b/lib/libesp32_div/esp-nimble-cpp/src/NimBLE2904.h @@ -1,42 +1,44 @@ /* - * NimBLE2904.h + * Copyright 2020-2025 Ryan Powell and + * esp-nimble-cpp, NimBLE-Arduino contributors. * - * Created: on March 13, 2020 - * Author H2zero + * 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 * - * Originally: + * http://www.apache.org/licenses/LICENSE-2.0 * - * BLE2904.h - * - * Created on: Dec 23, 2017 - * Author: kolban + * 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 MAIN_NIMBLE2904_H_ -#define MAIN_NIMBLE2904_H_ +#ifndef NIMBLE_CPP_2904_H_ +#define NIMBLE_CPP_2904_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" - -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; +# include "NimBLEDescriptor.h" +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)); - /** * @brief Descriptor for Characteristic Presentation Format. * * This is a convenience descriptor for the Characteristic Presentation Format which has a UUID of 0x2904. */ -class NimBLE2904: public NimBLEDescriptor { -public: - NimBLE2904(NimBLECharacteristic* pCharacterisitic = nullptr); +class NimBLE2904 : public NimBLEDescriptor { + public: + NimBLE2904(NimBLECharacteristic* pChr = nullptr); static const uint8_t FORMAT_BOOLEAN = 1; static const uint8_t FORMAT_UINT2 = 2; static const uint8_t FORMAT_UINT4 = 3; @@ -64,17 +66,18 @@ public: static const uint8_t FORMAT_UTF8 = 25; static const uint8_t FORMAT_UTF16 = 26; 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 setFormat(uint8_t format); void setNamespace(uint8_t namespace_value); void setUnit(uint16_t unit); -private: + private: friend class NimBLECharacteristic; - BLE2904_Data m_data; -}; // BLE2904 + NimBLE2904Data m_data{}; +}; // NimBLE2904 -#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL */ -#endif /* MAIN_NIMBLE2904_H_ */ +#endif // CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL +#endif // NIMBLE_CPP_2904_H_ diff --git a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEAddress.cpp b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEAddress.cpp index af7956b3c..0ff46e193 100644 --- a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEAddress.cpp +++ b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEAddress.cpp @@ -1,161 +1,185 @@ /* - * NimBLEAddress.cpp + * Copyright 2020-2025 Ryan Powell and + * esp-nimble-cpp, NimBLE-Arduino contributors. * - * Created: on Jan 24 2020 - * Author H2zero + * 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 * - * Originally: + * http://www.apache.org/licenses/LICENSE-2.0 * - * BLEAddress.cpp - * - * Created on: Jul 2, 2017 - * Author: kolban + * 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 "nimconfig.h" -#if defined(CONFIG_BT_ENABLED) - -#include #include "NimBLEAddress.h" -#include "NimBLEUtils.h" -#include "NimBLELog.h" +#if CONFIG_BT_ENABLED + +# include "NimBLELog.h" + +# include + +# 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"; /************************************************* * 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. * @param [in] address The native NimBLE address. */ -NimBLEAddress::NimBLEAddress(ble_addr_t address) { - memcpy(m_address, address.val, 6); - m_addrType = address.type; -} // NimBLEAddress - +NimBLEAddress::NimBLEAddress(ble_addr_t address) : ble_addr_t{address} {} /** - * @brief Create a blank address, i.e. 00:00:00:00:00:00, type 0. - */ -NimBLEAddress::NimBLEAddress() { - NimBLEAddress(""); -} // NimBLEAddress - - -/** - * @brief Create an address from a hex string + * @brief Create an address from a hex string. * * A hex string is of the format: * ``` * 00:00:00:00:00:00 * ``` * which is 17 characters in length. - * - * @param [in] stringAddress The hex string representation of the address. - * @param [in] type The type of the address. + * @param [in] addr The hex string representation 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 std::string &stringAddress, uint8_t type) { - m_addrType = type; +NimBLEAddress::NimBLEAddress(const std::string& addr, uint8_t type) { + this->type = type; - if (stringAddress.length() == 0) { - memset(m_address, 0, 6); + if (addr.length() == BLE_DEV_ADDR_LEN) { + std::reverse_copy(addr.data(), addr.data() + BLE_DEV_ADDR_LEN, this->val); return; } - if (stringAddress.length() == 6) { - std::reverse_copy(stringAddress.data(), stringAddress.data() + 6, m_address); + if (addr.length() == 17) { + 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; } - if (stringAddress.length() != 17) { - 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()); - 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]; - } + *this = NimBLEAddress{}; + NIMBLE_LOGE(LOG_TAG, "Invalid address '%s'", addr.c_str()); } // NimBLEAddress - /** * @brief Constructor for compatibility with bluedroid esp library using native ESP representation. * @param [in] address A uint8_t[6] or esp_bd_addr_t containing the address. - * @param [in] type The type of the address. + * @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) { - std::reverse_copy(address, address + sizeof m_address, m_address); - m_addrType = type; +NimBLEAddress::NimBLEAddress(const uint8_t address[BLE_DEV_ADDR_LEN], uint8_t type) { + std::reverse_copy(address, address + BLE_DEV_ADDR_LEN, this->val); + this->type = type; } // NimBLEAddress - /** * @brief Constructor for address using a hex value.\n * Use the same byte order, so use 0xa4c1385def16 for "a4:c1:38:5d:ef:16" * @param [in] address uint64_t containing the address. - * @param [in] type The type of the address. + * @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) { - memcpy(m_address, &address, sizeof m_address); - m_addrType = type; +NimBLEAddress::NimBLEAddress(const uint64_t& address, uint8_t type) { + memcpy(this->val, &address, sizeof(this->val)); + this->type = type; } // NimBLEAddress - /** * @brief Determine if this address equals another. * @param [in] otherAddress The other address to compare against. * @return True if the addresses are equal. */ -bool NimBLEAddress::equals(const NimBLEAddress &otherAddress) const { +bool NimBLEAddress::equals(const NimBLEAddress& otherAddress) const { return *this == otherAddress; } // equals - /** - * @brief Get the native representation of the address. - * @return a pointer to the uint8_t[6] array of the address. + * @brief Get the NimBLE base struct of the address. + * @return A read only reference to the NimBLE base struct of the address. */ -const uint8_t *NimBLEAddress::getNative() const { - return m_address; -} // getNative - +const ble_addr_t* NimBLEAddress::getBase() const { + return reinterpret_cast(this); +} // getBase /** * @brief Get the address type. * @return The address type. */ uint8_t NimBLEAddress::getType() const { - return m_addrType; + return this->type; } // 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. * @return True if the address is a RPA. */ bool NimBLEAddress::isRpa() const { - return (m_addrType && ((m_address[5] & 0xc0) == 0x40)); + return BLE_ADDR_IS_RPA(this); } // 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. - * - * A string representation of an address is in the format: - * - * ``` - * xx:xx:xx:xx:xx:xx - * ``` - * * @return The string representation of the address. * @deprecated Use std::string() operator instead. */ @@ -163,43 +187,62 @@ std::string NimBLEAddress::toString() const { return std::string(*this); } // 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. */ -bool NimBLEAddress::operator ==(const NimBLEAddress & rhs) const { - return memcmp(rhs.m_address, m_address, sizeof m_address) == 0; -} // operator == +bool NimBLEAddress::operator==(const NimBLEAddress& rhs) const { + if (this->type != rhs.type) { + 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. */ -bool NimBLEAddress::operator !=(const NimBLEAddress & rhs) const { +bool NimBLEAddress::operator!=(const NimBLEAddress& rhs) const { return !this->operator==(rhs); } // operator != - /** - * @brief Convienience operator to convert this address to string representation. - * @details This allows passing NimBLEAddress to functions - * that accept std::string and/or or it's methods as a parameter. + * @brief Convenience operator to convert this address to string representation. + * @details This allows passing NimBLEAddress to functions that accept std::string and/or it's methods as a parameter. */ NimBLEAddress::operator std::string() const { char buffer[18]; - snprintf(buffer, sizeof(buffer), "%02x:%02x:%02x:%02x:%02x:%02x", - m_address[5], m_address[4], m_address[3], - m_address[2], m_address[1], m_address[0]); - return std::string(buffer); + snprintf(buffer, + sizeof(buffer), + NIMBLE_CPP_ADDR_FMT, + 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 - /** * @brief Convenience operator to convert the native address representation to uint_64. */ NimBLEAddress::operator uint64_t() const { uint64_t address = 0; - memcpy(&address, m_address, sizeof m_address); + memcpy(&address, this->val, sizeof(this->val)); return address; } // operator uint64_t diff --git a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEAddress.h b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEAddress.h index 8a55b3ebf..58f00132b 100644 --- a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEAddress.h +++ b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEAddress.h @@ -1,63 +1,71 @@ /* - * NimBLEAddress.h + * Copyright 2020-2025 Ryan Powell and + * esp-nimble-cpp, NimBLE-Arduino contributors. * - * Created: on Jan 24 2020 - * Author H2zero + * 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 * - * Originally: + * http://www.apache.org/licenses/LICENSE-2.0 * - * BLEAddress.h - * - * Created on: Jul 2, 2017 - * Author: kolban + * 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 COMPONENTS_NIMBLEADDRESS_H_ -#define COMPONENTS_NIMBLEADDRESS_H_ -#include "nimconfig.h" -#if defined(CONFIG_BT_ENABLED) +#ifndef NIMBLE_CPP_ADDRESS_H_ +#define NIMBLE_CPP_ADDRESS_H_ -#if defined(CONFIG_NIMBLE_CPP_IDF) -#include "nimble/ble.h" -#else -#include "nimble/nimble/include/nimble/ble.h" -#endif +#include "nimconfig.h" +#if CONFIG_BT_ENABLED + +# if defined(CONFIG_NIMBLE_CPP_IDF) +# include "nimble/ble.h" +# else +# include "nimble/nimble/include/nimble/ble.h" +# endif /**** FIX COMPILATION ****/ -#undef min -#undef max +# undef min +# undef max /**************************/ -#include -#include +# include /** - * @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 { -public: - NimBLEAddress(); - NimBLEAddress(ble_addr_t address); - NimBLEAddress(uint8_t address[6], uint8_t type = BLE_ADDR_PUBLIC); - NimBLEAddress(const std::string &stringAddress, uint8_t type = BLE_ADDR_PUBLIC); - NimBLEAddress(const uint64_t &address, uint8_t type = BLE_ADDR_PUBLIC); - bool isRpa() const; - bool equals(const NimBLEAddress &otherAddress) const; - const uint8_t* getNative() const; - std::string toString() const; - uint8_t getType() const; +class NimBLEAddress : private ble_addr_t { + public: + /** + * @brief Create a blank address, i.e. 00:00:00:00:00:00, type 0. + */ + NimBLEAddress() = default; + NimBLEAddress(const ble_addr_t address); + NimBLEAddress(const uint8_t address[BLE_DEV_ADDR_LEN], uint8_t type); + NimBLEAddress(const std::string& stringAddress, uint8_t type); + NimBLEAddress(const uint64_t& address, uint8_t type); - bool operator ==(const NimBLEAddress & rhs) const; - bool operator !=(const NimBLEAddress & rhs) const; - operator std::string() const; - operator uint64_t() const; - -private: - uint8_t m_address[6]; - uint8_t m_addrType; + bool isRpa() const; + bool isNrpa() const; + bool isStatic() const; + bool isPublic() const; + bool isNull() const; + bool equals(const NimBLEAddress& otherAddress) const; + const ble_addr_t* getBase() const; + 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 /* COMPONENTS_NIMBLEADDRESS_H_ */ +#endif // CONFIG_BT_ENABLED +#endif // NIMBLE_CPP_ADDRESS_H_ diff --git a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEAdvertisedDevice.cpp b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEAdvertisedDevice.cpp index bdc1358eb..191b5c00d 100644 --- a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEAdvertisedDevice.cpp +++ b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEAdvertisedDevice.cpp @@ -1,53 +1,88 @@ /* - * NimBLEAdvertisedDevice.cpp + * Copyright 2020-2025 Ryan Powell and + * esp-nimble-cpp, NimBLE-Arduino contributors. * - * Created: on Jan 24 2020 - * Author H2zero + * 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 * - * Originally: + * http://www.apache.org/licenses/LICENSE-2.0 * - * BLEAdvertisedDevice.cpp - * - * Created on: Jul 3, 2017 - * Author: kolban + * 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 "nimconfig.h" -#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER) - -#include "NimBLEDevice.h" #include "NimBLEAdvertisedDevice.h" -#include "NimBLEUtils.h" -#include "NimBLELog.h" +#if CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_OBSERVER -#include +# include "NimBLEDevice.h" +# include "NimBLEUtils.h" +# include "NimBLELog.h" + +# include static const char* LOG_TAG = "NimBLEAdvertisedDevice"; - /** * @brief Constructor + * @param [in] event The advertisement event data. */ -NimBLEAdvertisedDevice::NimBLEAdvertisedDevice() : - m_payload(62,0) -{ - m_advType = 0; - m_rssi = -9999; - m_callbackSent = 0; - m_timestamp = 0; - m_advLength = 0; +NimBLEAdvertisedDevice::NimBLEAdvertisedDevice(const ble_gap_event* event, uint8_t eventType) +# if CONFIG_BT_NIMBLE_EXT_ADV + : m_address{event->ext_disc.addr}, + m_advType{eventType}, + m_rssi{event->ext_disc.rssi}, + m_callbackSent{0}, + m_advLength{event->ext_disc.length_data}, + m_isLegacyAdv{!!(event->ext_disc.props & BLE_HCI_ADV_LEGACY_MASK)}, + m_sid{event->ext_disc.sid}, + m_primPhy{event->ext_disc.prim_phy}, + m_secPhy{event->ext_disc.sec_phy}, + m_periodicItvl{event->ext_disc.periodic_adv_itvl}, + m_payload(event->ext_disc.data, event->ext_disc.data + event->ext_disc.length_data) { +# else + : m_address{event->disc.addr}, + m_advType{eventType}, + m_rssi{event->disc.rssi}, + m_callbackSent{0}, + m_advLength{event->disc.length_data}, + m_payload(event->disc.data, event->disc.data + event->disc.length_data) { +# endif } // NimBLEAdvertisedDevice +/** + * @brief Update the advertisement data. + * @param [in] event The advertisement event data. + */ +void NimBLEAdvertisedDevice::update(const ble_gap_event* event, uint8_t eventType) { +# if CONFIG_BT_NIMBLE_EXT_ADV + const auto& disc = event->ext_disc; + m_isLegacyAdv = disc.props & BLE_HCI_ADV_LEGACY_MASK; +# else + const auto& disc = event->disc; +# endif + + m_rssi = disc.rssi; + if (eventType == BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP && isLegacyAdvertisement()) { + m_payload.insert(m_payload.end(), disc.data, disc.data + disc.length_data); + return; + } + m_advLength = disc.length_data; + m_payload.assign(disc.data, disc.data + disc.length_data); + m_callbackSent = 0; // new data, reset callback sent flag +} // update /** * @brief Get the address of the advertising device. * @return The address of the advertised device. */ -NimBLEAddress NimBLEAdvertisedDevice::getAddress() { +const NimBLEAddress& NimBLEAdvertisedDevice::getAddress() const { return m_address; } // getAddress - /** * @brief Get the advertisement type. * @return The advertising type the device is reporting: @@ -57,11 +92,10 @@ NimBLEAddress NimBLEAdvertisedDevice::getAddress() { * * BLE_HCI_ADV_TYPE_ADV_NONCONN_IND (3) - indirect advertising - not connectable * * BLE_HCI_ADV_TYPE_ADV_DIRECT_IND_LD (4) - direct advertising - low duty cycle */ -uint8_t NimBLEAdvertisedDevice::getAdvType() { +uint8_t NimBLEAdvertisedDevice::getAdvType() const { return m_advType; } // getAdvType - /** * @brief Get the advertisement flags. * @return The advertisement flags, a bitmask of: @@ -69,15 +103,15 @@ uint8_t NimBLEAdvertisedDevice::getAdvType() { * BLE_HS_ADV_F_DISC_GEN (0x02) - general discoverability * BLE_HS_ADV_F_BREDR_UNSUP - BR/EDR not supported */ -uint8_t NimBLEAdvertisedDevice::getAdvFlags() { - size_t data_loc = 0; - - if(findAdvField(BLE_HS_ADV_TYPE_FLAGS, 0, &data_loc) > 0) { - ble_hs_adv_field *field = (ble_hs_adv_field *)&m_payload[data_loc]; - if(field->length == BLE_HS_ADV_FLAGS_LEN + 1) { +uint8_t NimBLEAdvertisedDevice::getAdvFlags() const { + size_t data_loc; + if (findAdvField(BLE_HS_ADV_TYPE_FLAGS, 0, &data_loc) > 0) { + const ble_hs_adv_field* field = reinterpret_cast(&m_payload[data_loc]); + if (field->length == BLE_HS_ADV_FLAGS_LEN + 1) { return *field->value; } } + return 0; } // getAdvFlags @@ -89,12 +123,11 @@ uint8_t NimBLEAdvertisedDevice::getAdvFlags() { * * @return The appearance of the advertised device. */ -uint16_t NimBLEAdvertisedDevice::getAppearance() { - size_t data_loc = 0; - - if(findAdvField(BLE_HS_ADV_TYPE_APPEARANCE, 0, &data_loc) > 0) { - ble_hs_adv_field *field = (ble_hs_adv_field *)&m_payload[data_loc]; - if(field->length == BLE_HS_ADV_APPEARANCE_LEN + 1) { +uint16_t NimBLEAdvertisedDevice::getAppearance() const { + size_t data_loc; + if (findAdvField(BLE_HS_ADV_TYPE_APPEARANCE, 0, &data_loc) > 0) { + const ble_hs_adv_field* field = reinterpret_cast(&m_payload[data_loc]); + if (field->length == BLE_HS_ADV_APPEARANCE_LEN + 1) { return *field->value | *(field->value + 1) << 8; } } @@ -102,17 +135,15 @@ uint16_t NimBLEAdvertisedDevice::getAppearance() { return 0; } // getAppearance - /** * @brief Get the advertisement interval. * @return The advertisement interval in 0.625ms units. */ -uint16_t NimBLEAdvertisedDevice::getAdvInterval() { - size_t data_loc = 0; - - if(findAdvField(BLE_HS_ADV_TYPE_ADV_ITVL, 0, &data_loc) > 0) { - ble_hs_adv_field *field = (ble_hs_adv_field *)&m_payload[data_loc]; - if(field->length == BLE_HS_ADV_ADV_ITVL_LEN + 1) { +uint16_t NimBLEAdvertisedDevice::getAdvInterval() const { + size_t data_loc; + if (findAdvField(BLE_HS_ADV_TYPE_ADV_ITVL, 0, &data_loc) > 0) { + const ble_hs_adv_field* field = reinterpret_cast(&m_payload[data_loc]); + if (field->length == BLE_HS_ADV_ADV_ITVL_LEN + 1) { return *field->value | *(field->value + 1) << 8; } } @@ -120,17 +151,15 @@ uint16_t NimBLEAdvertisedDevice::getAdvInterval() { return 0; } // getAdvInterval - /** * @brief Get the preferred min connection interval. * @return The preferred min connection interval in 1.25ms units. */ -uint16_t NimBLEAdvertisedDevice::getMinInterval() { - size_t data_loc = 0; - - if(findAdvField(BLE_HS_ADV_TYPE_SLAVE_ITVL_RANGE, 0, &data_loc) > 0) { - ble_hs_adv_field *field = (ble_hs_adv_field *)&m_payload[data_loc]; - if(field->length == BLE_HS_ADV_SLAVE_ITVL_RANGE_LEN + 1) { +uint16_t NimBLEAdvertisedDevice::getMinInterval() const { + size_t data_loc; + if (findAdvField(BLE_HS_ADV_TYPE_SLAVE_ITVL_RANGE, 0, &data_loc) > 0) { + const ble_hs_adv_field* field = reinterpret_cast(&m_payload[data_loc]); + if (field->length == BLE_HS_ADV_SLAVE_ITVL_RANGE_LEN + 1) { return *field->value | *(field->value + 1) << 8; } } @@ -138,17 +167,15 @@ uint16_t NimBLEAdvertisedDevice::getMinInterval() { return 0; } // getMinInterval - /** * @brief Get the preferred max connection interval. * @return The preferred max connection interval in 1.25ms units. */ -uint16_t NimBLEAdvertisedDevice::getMaxInterval() { - size_t data_loc = 0; - - if(findAdvField(BLE_HS_ADV_TYPE_SLAVE_ITVL_RANGE, 0, &data_loc) > 0) { - ble_hs_adv_field *field = (ble_hs_adv_field *)&m_payload[data_loc]; - if(field->length == BLE_HS_ADV_SLAVE_ITVL_RANGE_LEN + 1) { +uint16_t NimBLEAdvertisedDevice::getMaxInterval() const { + size_t data_loc; + if (findAdvField(BLE_HS_ADV_TYPE_SLAVE_ITVL_RANGE, 0, &data_loc) > 0) { + const ble_hs_adv_field* field = reinterpret_cast(&m_payload[data_loc]); + if (field->length == BLE_HS_ADV_SLAVE_ITVL_RANGE_LEN + 1) { return *(field->value + 2) | *(field->value + 3) << 8; } } @@ -156,64 +183,42 @@ uint16_t NimBLEAdvertisedDevice::getMaxInterval() { return 0; } // getMaxInterval - /** * @brief Get the manufacturer data. * @param [in] index The index of the of the manufacturer data set to get. * @return The manufacturer data. */ -std::string NimBLEAdvertisedDevice::getManufacturerData(uint8_t index) { - size_t data_loc = 0; - index++; - - if(findAdvField(BLE_HS_ADV_TYPE_MFG_DATA, index, &data_loc) > 0) { - ble_hs_adv_field *field = (ble_hs_adv_field *)&m_payload[data_loc]; - if(field->length > 1) { - return std::string((char*)field->value, field->length - 1); - } - } - - return ""; +std::string NimBLEAdvertisedDevice::getManufacturerData(uint8_t index) const { + return getPayloadByType(BLE_HS_ADV_TYPE_MFG_DATA, index); } // getManufacturerData - /** * @brief Get the count of manufacturer data sets. * @return The number of manufacturer data sets. */ -uint8_t NimBLEAdvertisedDevice::getManufacturerDataCount() { +uint8_t NimBLEAdvertisedDevice::getManufacturerDataCount() const { return findAdvField(BLE_HS_ADV_TYPE_MFG_DATA); } // getManufacturerDataCount - /** * @brief Get the URI from the advertisement. * @return The URI data. */ -std::string NimBLEAdvertisedDevice::getURI() { - size_t data_loc = 0; - - if(findAdvField(BLE_HS_ADV_TYPE_URI, 0, &data_loc) > 0) { - ble_hs_adv_field *field = (ble_hs_adv_field *)&m_payload[data_loc]; - if(field->length > 1) { - return std::string((char*)field->value, field->length - 1); - } - } - - return ""; +std::string NimBLEAdvertisedDevice::getURI() const { + return getPayloadByType(BLE_HS_ADV_TYPE_URI); } // getURI /** - * @brief Get the data from any type available in the advertisement - * @param [in] type The advertised data type BLE_HS_ADV_TYPE - * @return The data available under the type `type` -*/ -std::string NimBLEAdvertisedDevice::getPayloadByType(uint16_t type) { - size_t data_loc = 0; - - if(findAdvField(type, 0, &data_loc) > 0) { - ble_hs_adv_field *field = (ble_hs_adv_field *)&m_payload[data_loc]; - if(field->length > 1) { + * @brief Get the data from any type available in the advertisement. + * @param [in] type The advertised data type BLE_HS_ADV_TYPE. + * @param [in] index The index of the data type. + * @return The data available under the type `type`. + */ +std::string NimBLEAdvertisedDevice::getPayloadByType(uint16_t type, uint8_t index) const { + size_t data_loc; + if (findAdvField(type, index, &data_loc) > 0) { + const ble_hs_adv_field* field = reinterpret_cast(&m_payload[data_loc]); + if (field->length > 1) { return std::string((char*)field->value, field->length - 1); } } @@ -221,129 +226,105 @@ std::string NimBLEAdvertisedDevice::getPayloadByType(uint16_t type) { return ""; } // getPayloadByType - /** * @brief Get the advertised name. * @return The name of the advertised device. */ -std::string NimBLEAdvertisedDevice::getName() { - size_t data_loc = 0; - - if(findAdvField(BLE_HS_ADV_TYPE_COMP_NAME, 0, &data_loc) > 0 || - findAdvField(BLE_HS_ADV_TYPE_INCOMP_NAME, 0, &data_loc) > 0) - { - ble_hs_adv_field *field = (ble_hs_adv_field *)&m_payload[data_loc]; - if(field->length > 1) { - return std::string((char*)field->value, field->length - 1); - } - } - - return ""; +std::string NimBLEAdvertisedDevice::getName() const { + return getPayloadByType(BLE_HS_ADV_TYPE_COMP_NAME); } // getName - /** * @brief Get the RSSI. * @return The RSSI of the advertised device. */ -int NimBLEAdvertisedDevice::getRSSI() { +int8_t NimBLEAdvertisedDevice::getRSSI() const { return m_rssi; } // getRSSI - /** * @brief Get the scan object that created this advertised device. * @return The scan object. */ -NimBLEScan* NimBLEAdvertisedDevice::getScan() { +NimBLEScan* NimBLEAdvertisedDevice::getScan() const { return NimBLEDevice::getScan(); } // getScan - /** * @brief Get the number of target addresses. * @return The number of addresses. */ -uint8_t NimBLEAdvertisedDevice::getTargetAddressCount() { - uint8_t count = 0; - - count = findAdvField(BLE_HS_ADV_TYPE_PUBLIC_TGT_ADDR); - count += findAdvField(BLE_HS_ADV_TYPE_RANDOM_TGT_ADDR); +uint8_t NimBLEAdvertisedDevice::getTargetAddressCount() const { + uint8_t count = findAdvField(BLE_HS_ADV_TYPE_PUBLIC_TGT_ADDR); + count += findAdvField(BLE_HS_ADV_TYPE_RANDOM_TGT_ADDR); return count; } - /** * @brief Get the target address at the index. * @param [in] index The index of the target address. * @return The target address. */ -NimBLEAddress NimBLEAdvertisedDevice::getTargetAddress(uint8_t index) { - ble_hs_adv_field *field = nullptr; - uint8_t count = 0; - size_t data_loc = ULONG_MAX; - - index++; - count = findAdvField(BLE_HS_ADV_TYPE_PUBLIC_TGT_ADDR, index, &data_loc); - - if (count < index) { +NimBLEAddress NimBLEAdvertisedDevice::getTargetAddress(uint8_t index) const { + size_t data_loc = ULONG_MAX; + uint8_t count = findAdvField(BLE_HS_ADV_TYPE_PUBLIC_TGT_ADDR, index, &data_loc); + if (count < index + 1) { index -= count; - count = findAdvField(BLE_HS_ADV_TYPE_RANDOM_TGT_ADDR, index, &data_loc); + count = findAdvField(BLE_HS_ADV_TYPE_RANDOM_TGT_ADDR, index, &data_loc); } - if(count > 0 && data_loc != ULONG_MAX) { - field = (ble_hs_adv_field *)&m_payload[data_loc]; - if(field->length < index * BLE_HS_ADV_PUBLIC_TGT_ADDR_ENTRY_LEN) { + if (count > 0 && data_loc != ULONG_MAX) { + index++; + const ble_hs_adv_field* field = reinterpret_cast(&m_payload[data_loc]); + if (field->length < index * BLE_HS_ADV_PUBLIC_TGT_ADDR_ENTRY_LEN) { + // In the case of more than one field of target addresses we need to adjust the index index -= count - field->length / BLE_HS_ADV_PUBLIC_TGT_ADDR_ENTRY_LEN; } - if(field->length > index * BLE_HS_ADV_PUBLIC_TGT_ADDR_ENTRY_LEN) { - return NimBLEAddress(field->value + (index - 1) * BLE_HS_ADV_PUBLIC_TGT_ADDR_ENTRY_LEN); + if (field->length > index * BLE_HS_ADV_PUBLIC_TGT_ADDR_ENTRY_LEN) { + return NimBLEAddress{field->value + (index - 1) * BLE_HS_ADV_PUBLIC_TGT_ADDR_ENTRY_LEN, field->type}; } } - return NimBLEAddress(""); + return NimBLEAddress{}; } - /** * @brief Get the service data. * @param [in] index The index of the service data requested. * @return The advertised service data or empty string if no data. */ -std::string NimBLEAdvertisedDevice::getServiceData(uint8_t index) { - ble_hs_adv_field *field = nullptr; +std::string NimBLEAdvertisedDevice::getServiceData(uint8_t index) const { uint8_t bytes; - size_t data_loc = findServiceData(index, &bytes); - - if(data_loc != ULONG_MAX) { - field = (ble_hs_adv_field *)&m_payload[data_loc]; - if(field->length > bytes) { - return std::string((char*)(field->value + bytes), field->length - bytes - 1); + size_t data_loc = findServiceData(index, &bytes); + if (data_loc != ULONG_MAX) { + const ble_hs_adv_field* field = reinterpret_cast(&m_payload[data_loc]); + if (field->length > bytes) { + const char* field_data = reinterpret_cast(field->value + bytes); + return std::string(field_data, field->length - bytes - 1); } } return ""; -} //getServiceData - +} // getServiceData /** * @brief Get the service data. * @param [in] uuid The uuid of the service data requested. * @return The advertised service data or empty string if no data. */ -std::string NimBLEAdvertisedDevice::getServiceData(const NimBLEUUID &uuid) { - ble_hs_adv_field *field = nullptr; +std::string NimBLEAdvertisedDevice::getServiceData(const NimBLEUUID& uuid) const { uint8_t bytes; - uint8_t index = 0; - size_t data_loc = findServiceData(index, &bytes); - size_t plSize = m_payload.size() - 2; - uint8_t uuidBytes = uuid.bitSize() / 8; + uint8_t index = 0; + size_t data_loc = findServiceData(index, &bytes); + size_t pl_size = m_payload.size() - 2; + uint8_t uuid_bytes = uuid.bitSize() / 8; - while(data_loc < plSize) { - field = (ble_hs_adv_field *)&m_payload[data_loc]; - if(bytes == uuidBytes && NimBLEUUID(field->value, bytes, false) == uuid) { - return std::string((char*)(field->value + bytes), field->length - bytes - 1); + while (data_loc < pl_size) { + const ble_hs_adv_field* field = reinterpret_cast(&m_payload[data_loc]); + if (bytes == uuid_bytes && NimBLEUUID(field->value, bytes) == uuid) { + const char* field_data = reinterpret_cast(field->value + bytes); + return std::string(field_data, field->length - bytes - 1); } index++; @@ -352,58 +333,52 @@ std::string NimBLEAdvertisedDevice::getServiceData(const NimBLEUUID &uuid) { NIMBLE_LOGI(LOG_TAG, "No service data found"); return ""; -} //getServiceData - +} // getServiceData /** * @brief Get the UUID of the service data at the index. * @param [in] index The index of the service data UUID requested. * @return The advertised service data UUID or an empty UUID if not found. */ -NimBLEUUID NimBLEAdvertisedDevice::getServiceDataUUID(uint8_t index) { - ble_hs_adv_field *field = nullptr; +NimBLEUUID NimBLEAdvertisedDevice::getServiceDataUUID(uint8_t index) const { uint8_t bytes; - size_t data_loc = findServiceData(index, &bytes); - - if(data_loc != ULONG_MAX) { - field = (ble_hs_adv_field *)&m_payload[data_loc]; - if(field->length >= bytes) { - return NimBLEUUID(field->value, bytes, false); + size_t data_loc = findServiceData(index, &bytes); + if (data_loc != ULONG_MAX) { + const ble_hs_adv_field* field = reinterpret_cast(&m_payload[data_loc]); + if (field->length >= bytes) { + return NimBLEUUID(field->value, bytes); } } return NimBLEUUID(""); } // getServiceDataUUID - /** * @brief Find the service data at the index. * @param [in] index The index of the service data to find. * @param [in] bytes A pointer to storage for the number of the bytes in the UUID. * @return The index in the vector where the data is located, ULONG_MAX if not found. */ -size_t NimBLEAdvertisedDevice::findServiceData(uint8_t index, uint8_t *bytes) { - size_t data_loc = 0; - uint8_t found = 0; - +size_t NimBLEAdvertisedDevice::findServiceData(uint8_t index, uint8_t* bytes) const { *bytes = 0; - index++; - found = findAdvField(BLE_HS_ADV_TYPE_SVC_DATA_UUID16, index, &data_loc); - if(found == index) { + + size_t data_loc = 0; + uint8_t found = findAdvField(BLE_HS_ADV_TYPE_SVC_DATA_UUID16, index, &data_loc); + if (found > index) { *bytes = 2; return data_loc; } index -= found; - found = findAdvField(BLE_HS_ADV_TYPE_SVC_DATA_UUID32, index, &data_loc); - if(found == index) { + found = findAdvField(BLE_HS_ADV_TYPE_SVC_DATA_UUID32, index, &data_loc); + if (found > index) { *bytes = 4; return data_loc; } index -= found; - found = findAdvField(BLE_HS_ADV_TYPE_SVC_DATA_UUID128, index, &data_loc); - if(found == index) { + found = findAdvField(BLE_HS_ADV_TYPE_SVC_DATA_UUID128, index, &data_loc); + if (found > index) { *bytes = 16; return data_loc; } @@ -411,45 +386,38 @@ size_t NimBLEAdvertisedDevice::findServiceData(uint8_t index, uint8_t *bytes) { return ULONG_MAX; } - /** * @brief Get the count of advertised service data UUIDS * @return The number of service data UUIDS in the vector. */ -uint8_t NimBLEAdvertisedDevice::getServiceDataCount() { - uint8_t count = 0; - - count += findAdvField(BLE_HS_ADV_TYPE_SVC_DATA_UUID16); - count += findAdvField(BLE_HS_ADV_TYPE_SVC_DATA_UUID32); - count += findAdvField(BLE_HS_ADV_TYPE_SVC_DATA_UUID128); +uint8_t NimBLEAdvertisedDevice::getServiceDataCount() const { + uint8_t count = findAdvField(BLE_HS_ADV_TYPE_SVC_DATA_UUID16); + count += findAdvField(BLE_HS_ADV_TYPE_SVC_DATA_UUID32); + count += findAdvField(BLE_HS_ADV_TYPE_SVC_DATA_UUID128); return count; } // getServiceDataCount - /** * @brief Get the Service UUID. * @param [in] index The index of the service UUID requested. * @return The Service UUID of the advertised service, or an empty UUID if not found. */ -NimBLEUUID NimBLEAdvertisedDevice::getServiceUUID(uint8_t index) { - uint8_t count = 0; - size_t data_loc = 0; - uint8_t uuidBytes = 0; - uint8_t type = BLE_HS_ADV_TYPE_INCOMP_UUIDS16; - ble_hs_adv_field *field = nullptr; - - index++; +NimBLEUUID NimBLEAdvertisedDevice::getServiceUUID(uint8_t index) const { + uint8_t type = BLE_HS_ADV_TYPE_INCOMP_UUIDS16; + size_t data_loc = 0; + uint8_t uuid_bytes = 0; + uint8_t count = 0; do { count = findAdvField(type, index, &data_loc); - if(count >= index) { - if(type < BLE_HS_ADV_TYPE_INCOMP_UUIDS32) { - uuidBytes = 2; - } else if(type < BLE_HS_ADV_TYPE_INCOMP_UUIDS128) { - uuidBytes = 4; + if (count > index) { + if (type < BLE_HS_ADV_TYPE_INCOMP_UUIDS32) { + uuid_bytes = 2; + } else if (type < BLE_HS_ADV_TYPE_INCOMP_UUIDS128) { + uuid_bytes = 4; } else { - uuidBytes = 16; + uuid_bytes = 16; } break; @@ -458,52 +426,49 @@ NimBLEUUID NimBLEAdvertisedDevice::getServiceUUID(uint8_t index) { index -= count; } - } while(type <= BLE_HS_ADV_TYPE_COMP_UUIDS128); + } while (type <= BLE_HS_ADV_TYPE_COMP_UUIDS128); - if(uuidBytes > 0) { - field = (ble_hs_adv_field *)&m_payload[data_loc]; + if (uuid_bytes > 0) { + index++; + const ble_hs_adv_field* field = reinterpret_cast(&m_payload[data_loc]); // In the case of more than one field of service uuid's we need to adjust // the index to account for the uuids of the previous fields. - if(field->length < index * uuidBytes) { - index -= count - field->length / uuidBytes; + if (field->length < index * uuid_bytes) { + index -= count - field->length / uuid_bytes; } - if(field->length > uuidBytes * index) { - return NimBLEUUID(field->value + uuidBytes * (index - 1), uuidBytes, false); + if (field->length > uuid_bytes * index) { + return NimBLEUUID(field->value + uuid_bytes * (index - 1), uuid_bytes); } } return NimBLEUUID(""); } // getServiceUUID - /** * @brief Get the number of services advertised * @return The count of services in the advertising packet. */ -uint8_t NimBLEAdvertisedDevice::getServiceUUIDCount() { - uint8_t count = 0; - - count += findAdvField(BLE_HS_ADV_TYPE_INCOMP_UUIDS16); - count += findAdvField(BLE_HS_ADV_TYPE_COMP_UUIDS16); - count += findAdvField(BLE_HS_ADV_TYPE_INCOMP_UUIDS32); - count += findAdvField(BLE_HS_ADV_TYPE_COMP_UUIDS32); - count += findAdvField(BLE_HS_ADV_TYPE_INCOMP_UUIDS128); - count += findAdvField(BLE_HS_ADV_TYPE_COMP_UUIDS128); +uint8_t NimBLEAdvertisedDevice::getServiceUUIDCount() const { + uint8_t count = findAdvField(BLE_HS_ADV_TYPE_INCOMP_UUIDS16); + count += findAdvField(BLE_HS_ADV_TYPE_COMP_UUIDS16); + count += findAdvField(BLE_HS_ADV_TYPE_INCOMP_UUIDS32); + count += findAdvField(BLE_HS_ADV_TYPE_COMP_UUIDS32); + count += findAdvField(BLE_HS_ADV_TYPE_INCOMP_UUIDS128); + count += findAdvField(BLE_HS_ADV_TYPE_COMP_UUIDS128); return count; } // getServiceUUIDCount - /** * @brief Check advertised services for existence of the required UUID * @param [in] uuid The service uuid to look for in the advertisement. * @return Return true if service is advertised */ -bool NimBLEAdvertisedDevice::isAdvertisingService(const NimBLEUUID &uuid) { +bool NimBLEAdvertisedDevice::isAdvertisingService(const NimBLEUUID& uuid) const { size_t count = getServiceUUIDCount(); - for(size_t i = 0; i < count; i++) { - if(uuid == getServiceUUID(i)) { + for (size_t i = 0; i < count; i++) { + if (uuid == getServiceUUID(i)) { return true; } } @@ -511,17 +476,15 @@ bool NimBLEAdvertisedDevice::isAdvertisingService(const NimBLEUUID &uuid) { return false; } // isAdvertisingService - /** * @brief Get the TX Power. * @return The TX Power of the advertised device. */ -int8_t NimBLEAdvertisedDevice::getTXPower() { +int8_t NimBLEAdvertisedDevice::getTXPower() const { size_t data_loc = 0; - - if(findAdvField(BLE_HS_ADV_TYPE_TX_PWR_LVL, 0, &data_loc) > 0) { - ble_hs_adv_field *field = (ble_hs_adv_field *)&m_payload[data_loc]; - if(field->length == BLE_HS_ADV_TX_PWR_LVL_LEN + 1) { + if (findAdvField(BLE_HS_ADV_TYPE_TX_PWR_LVL, 0, &data_loc) > 0) { + const ble_hs_adv_field* field = reinterpret_cast(&m_payload[data_loc]); + if (field->length == BLE_HS_ADV_TX_PWR_LVL_LEN + 1) { return *(int8_t*)field->value; } } @@ -529,137 +492,113 @@ int8_t NimBLEAdvertisedDevice::getTXPower() { return -99; } // getTXPower - /** * @brief Does this advertisement have preferred connection parameters? * @return True if connection parameters are present. */ -bool NimBLEAdvertisedDevice::haveConnParams() { +bool NimBLEAdvertisedDevice::haveConnParams() const { return findAdvField(BLE_HS_ADV_TYPE_SLAVE_ITVL_RANGE) > 0; } // haveConnParams - /** * @brief Does this advertisement have have the advertising interval? * @return True if the advertisement interval is present. */ -bool NimBLEAdvertisedDevice::haveAdvInterval() { +bool NimBLEAdvertisedDevice::haveAdvInterval() const { return findAdvField(BLE_HS_ADV_TYPE_ADV_ITVL) > 0; } // haveAdvInterval - /** * @brief Does this advertisement have an appearance value? * @return True if there is an appearance value present. */ -bool NimBLEAdvertisedDevice::haveAppearance() { +bool NimBLEAdvertisedDevice::haveAppearance() const { return findAdvField(BLE_HS_ADV_TYPE_APPEARANCE) > 0; } // haveAppearance - /** * @brief Does this advertisement have manufacturer data? * @return True if there is manufacturer data present. */ -bool NimBLEAdvertisedDevice::haveManufacturerData() { +bool NimBLEAdvertisedDevice::haveManufacturerData() const { return findAdvField(BLE_HS_ADV_TYPE_MFG_DATA) > 0; } // haveManufacturerData - /** * @brief Does this advertisement have a URI? * @return True if there is a URI present. */ -bool NimBLEAdvertisedDevice::haveURI() { +bool NimBLEAdvertisedDevice::haveURI() const { return findAdvField(BLE_HS_ADV_TYPE_URI) > 0; } // haveURI /** * @brief Does this advertisement have a adv type `type`? * @return True if there is a `type` present. -*/ -bool NimBLEAdvertisedDevice::haveType(uint16_t type) { + */ +bool NimBLEAdvertisedDevice::haveType(uint16_t type) const { return findAdvField(type) > 0; } - /** * @brief Does the advertisement contain a target address? * @return True if an address is present. */ -bool NimBLEAdvertisedDevice::haveTargetAddress() { - return findAdvField(BLE_HS_ADV_TYPE_PUBLIC_TGT_ADDR) > 0 || - findAdvField(BLE_HS_ADV_TYPE_RANDOM_TGT_ADDR) > 0; +bool NimBLEAdvertisedDevice::haveTargetAddress() const { + return findAdvField(BLE_HS_ADV_TYPE_PUBLIC_TGT_ADDR) > 0 || findAdvField(BLE_HS_ADV_TYPE_RANDOM_TGT_ADDR) > 0; } - /** * @brief Does this advertisement have a name value? * @return True if there is a name value present. */ -bool NimBLEAdvertisedDevice::haveName() { - return findAdvField(BLE_HS_ADV_TYPE_COMP_NAME) > 0 || - findAdvField(BLE_HS_ADV_TYPE_INCOMP_NAME) > 0; +bool NimBLEAdvertisedDevice::haveName() const { + return findAdvField(BLE_HS_ADV_TYPE_COMP_NAME) > 0; } // haveName - -/** - * @brief Does this advertisement have a signal strength value? - * @return True if there is a signal strength value present. - */ -bool NimBLEAdvertisedDevice::haveRSSI() { - return m_rssi != -9999; -} // haveRSSI - - /** * @brief Does this advertisement have a service data value? * @return True if there is a service data value present. */ -bool NimBLEAdvertisedDevice::haveServiceData() { +bool NimBLEAdvertisedDevice::haveServiceData() const { return getServiceDataCount() > 0; } // haveServiceData - /** * @brief Does this advertisement have a service UUID value? * @return True if there is a service UUID value present. */ -bool NimBLEAdvertisedDevice::haveServiceUUID() { +bool NimBLEAdvertisedDevice::haveServiceUUID() const { return getServiceUUIDCount() > 0; } // haveServiceUUID - /** * @brief Does this advertisement have a transmission power value? * @return True if there is a transmission power value present. */ -bool NimBLEAdvertisedDevice::haveTXPower() { +bool NimBLEAdvertisedDevice::haveTXPower() const { return findAdvField(BLE_HS_ADV_TYPE_TX_PWR_LVL) > 0; } // haveTXPower - -#if CONFIG_BT_NIMBLE_EXT_ADV +# if CONFIG_BT_NIMBLE_EXT_ADV /** * @brief Get the set ID of the extended advertisement. * @return The set ID. */ -uint8_t NimBLEAdvertisedDevice::getSetId() { +uint8_t NimBLEAdvertisedDevice::getSetId() const { return m_sid; } // getSetId - /** * @brief Get the primary PHY used by this advertisement. * @return The PHY type, one of: * * BLE_HCI_LE_PHY_1M * * BLE_HCI_LE_PHY_CODED */ -uint8_t NimBLEAdvertisedDevice::getPrimaryPhy() { +uint8_t NimBLEAdvertisedDevice::getPrimaryPhy() const { return m_primPhy; } // getPrimaryPhy - /** * @brief Get the primary PHY used by this advertisement. * @return The PHY type, one of: @@ -667,39 +606,31 @@ uint8_t NimBLEAdvertisedDevice::getPrimaryPhy() { * * BLE_HCI_LE_PHY_2M * * BLE_HCI_LE_PHY_CODED */ -uint8_t NimBLEAdvertisedDevice::getSecondaryPhy() { +uint8_t NimBLEAdvertisedDevice::getSecondaryPhy() const { return m_secPhy; } // getSecondaryPhy - /** * @brief Get the periodic interval of the advertisement. * @return The periodic advertising interval, 0 if not periodic advertising. */ -uint16_t NimBLEAdvertisedDevice::getPeriodicInterval() { +uint16_t NimBLEAdvertisedDevice::getPeriodicInterval() const { return m_periodicItvl; } // getPeriodicInterval -#endif +# endif - -uint8_t NimBLEAdvertisedDevice::findAdvField(uint8_t type, uint8_t index, size_t * data_loc) { - ble_hs_adv_field *field = nullptr; +uint8_t NimBLEAdvertisedDevice::findAdvField(uint8_t type, uint8_t index, size_t* data_loc) const { size_t length = m_payload.size(); size_t data = 0; uint8_t count = 0; - if (length < 3) { - return count; - } - while (length > 2) { - field = (ble_hs_adv_field*)&m_payload[data]; - + const ble_hs_adv_field* field = reinterpret_cast(&m_payload[data]); if (field->length >= length) { return count; } - if (field->type == type) { + if (field->type == type || (type == BLE_HS_ADV_TYPE_COMP_NAME && field->type == BLE_HS_ADV_TYPE_INCOMP_NAME)) { switch (type) { case BLE_HS_ADV_TYPE_INCOMP_UUIDS16: case BLE_HS_ADV_TYPE_COMP_UUIDS16: @@ -721,67 +652,41 @@ uint8_t NimBLEAdvertisedDevice::findAdvField(uint8_t type, uint8_t index, size_t count += field->length / 6; break; + case BLE_HS_ADV_TYPE_COMP_NAME: + // keep looking for complete name, else use this + if (data_loc != nullptr && field->type == BLE_HS_ADV_TYPE_INCOMP_NAME) { + *data_loc = data; + index++; + } + // fall through default: count++; break; } if (data_loc != nullptr) { - if (index == 0 || count >= index) { + if (count > index) { // assumes index values default to 0 break; } } } length -= 1 + field->length; - data += 1 + field->length; + data += 1 + field->length; } - if (data_loc != nullptr && field != nullptr) { + if (data_loc != nullptr && count > index) { *data_loc = data; } return count; -} - - -/** - * @brief Set the address of the advertised device. - * @param [in] address The address of the advertised device. - */ -void NimBLEAdvertisedDevice::setAddress(NimBLEAddress address) { - m_address = address; -} // setAddress - - -/** - * @brief Set the adFlag for this device. - * @param [in] advType The advertisement flag data from the advertisement. - */ -void NimBLEAdvertisedDevice::setAdvType(uint8_t advType, bool isLegacyAdv) { - m_advType = advType; -#if CONFIG_BT_NIMBLE_EXT_ADV - m_isLegacyAdv = isLegacyAdv; -#else - (void)isLegacyAdv; -#endif -} // setAdvType - - -/** - * @brief Set the RSSI for this device. - * @param [in] rssi The RSSI of the discovered device. - */ -void NimBLEAdvertisedDevice::setRSSI(int rssi) { - m_rssi = rssi; -} // setRSSI - +} // findAdvField /** * @brief Create a string representation of this device. * @return A string representation of this device. */ -std::string NimBLEAdvertisedDevice::toString() { +std::string NimBLEAdvertisedDevice::toString() const { std::string res = "Name: " + getName() + ", Address: " + getAddress().toString(); if (haveAppearance()) { @@ -792,10 +697,9 @@ std::string NimBLEAdvertisedDevice::toString() { } if (haveManufacturerData()) { - char *pHex = NimBLEUtils::buildHexData(nullptr, (uint8_t*)getManufacturerData().data(), getManufacturerData().length()); - res += ", manufacturer data: "; - res += pHex; - free(pHex); + auto mfgData = getManufacturerData(); + res += ", manufacturer data: "; + res += NimBLEUtils::dataToHexString(reinterpret_cast(mfgData.data()), mfgData.length()); } if (haveServiceUUID()) { @@ -810,9 +714,9 @@ std::string NimBLEAdvertisedDevice::toString() { } if (haveServiceData()) { - uint8_t count = getServiceDataCount(); - res += "\nService Data:"; - for(uint8_t i = 0; i < count; i++) { + uint8_t count = getServiceDataCount(); + res += "\nService Data:"; + for (uint8_t i = 0; i < count; i++) { res += "\nUUID: " + std::string(getServiceDataUUID(i)); res += ", Data: " + getServiceData(i); } @@ -822,41 +726,14 @@ std::string NimBLEAdvertisedDevice::toString() { } // toString - -/** - * @brief Get the payload advertised by the device. - * @return The advertisement payload. - */ -uint8_t* NimBLEAdvertisedDevice::getPayload() { - return &m_payload[0]; -} // getPayload - - -/** - * @brief Stores the payload of the advertised device in a vector. - * @param [in] payload The advertisement payload. - * @param [in] length The length of the payload in bytes. - * @param [in] append Indicates if the the data should be appended (scan response). - */ -void NimBLEAdvertisedDevice::setPayload(const uint8_t *payload, uint8_t length, bool append) { - if(!append) { - m_advLength = length; - m_payload.assign(payload, payload + length); - } else { - m_payload.insert(m_payload.end(), payload, payload + length); - } -} - - /** * @brief Get the length of the advertisement data in the payload. * @return The number of bytes in the payload that is from the advertisement. */ -uint8_t NimBLEAdvertisedDevice::getAdvLength() { +uint8_t NimBLEAdvertisedDevice::getAdvLength() const { return m_advLength; } - /** * @brief Get the advertised device address type. * @return The device address type: @@ -865,56 +742,75 @@ uint8_t NimBLEAdvertisedDevice::getAdvLength() { * * BLE_ADDR_PUBLIC_ID (0x02) * * BLE_ADDR_RANDOM_ID (0x03) */ -uint8_t NimBLEAdvertisedDevice::getAddressType() { +uint8_t NimBLEAdvertisedDevice::getAddressType() const { return m_address.getType(); } // getAddressType - -/** - * @brief Get the timeStamp of when the device last advertised. - * @return The timeStamp of when the device was last seen. - */ -time_t NimBLEAdvertisedDevice::getTimestamp() { - return m_timestamp; -} // getTimestamp - - -/** - * @brief Get the length of the payload advertised by the device. - * @return The size of the payload in bytes. - */ -size_t NimBLEAdvertisedDevice::getPayloadLength() { - return m_payload.size(); -} // getPayloadLength - - /** * @brief Check if this device is advertising as connectable. * @return True if the device is connectable. */ -bool NimBLEAdvertisedDevice::isConnectable() { -#if CONFIG_BT_NIMBLE_EXT_ADV +bool NimBLEAdvertisedDevice::isConnectable() const { +# if CONFIG_BT_NIMBLE_EXT_ADV if (m_isLegacyAdv) { - return m_advType == BLE_HCI_ADV_RPT_EVTYPE_ADV_IND || - m_advType == BLE_HCI_ADV_RPT_EVTYPE_DIR_IND; + return m_advType == BLE_HCI_ADV_RPT_EVTYPE_ADV_IND || m_advType == BLE_HCI_ADV_RPT_EVTYPE_DIR_IND; } -#endif - return (m_advType & BLE_HCI_ADV_CONN_MASK) || - (m_advType & BLE_HCI_ADV_DIRECT_MASK); +# endif + return (m_advType & BLE_HCI_ADV_CONN_MASK) || (m_advType & BLE_HCI_ADV_DIRECT_MASK); } // isConnectable +/** + * @brief Check if this device is advertising as scannable. + * @return True if the device is scannable. + */ +bool NimBLEAdvertisedDevice::isScannable() const { + return isLegacyAdvertisement() && (m_advType == BLE_HCI_ADV_TYPE_ADV_IND || m_advType == BLE_HCI_ADV_TYPE_ADV_SCAN_IND); +} // isScannable /** * @brief Check if this advertisement is a legacy or extended type * @return True if legacy (Bluetooth 4.x), false if extended (bluetooth 5.x). */ -bool NimBLEAdvertisedDevice::isLegacyAdvertisement() { -#if CONFIG_BT_NIMBLE_EXT_ADV +bool NimBLEAdvertisedDevice::isLegacyAdvertisement() const { +# if CONFIG_BT_NIMBLE_EXT_ADV return m_isLegacyAdv; # else return true; -#endif +# endif } // isLegacyAdvertisement -#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL */ +/** + * @brief Convenience operator to convert this NimBLEAdvertisedDevice to NimBLEAddress representation. + * @details This allows passing NimBLEAdvertisedDevice to functions + * that accept NimBLEAddress and/or or it's methods as a parameter. + */ +NimBLEAdvertisedDevice::operator NimBLEAddress() const { + NimBLEAddress address(getAddress()); + return address; +} // operator NimBLEAddress +/** + * @brief Get the payload advertised by the device. + * @return The advertisement payload. + */ +const std::vector& NimBLEAdvertisedDevice::getPayload() const { + return m_payload; +} + +/** + * @brief Get the begin iterator for the payload. + * @return A read only iterator pointing to the first byte in the payload. + */ +const std::vector::const_iterator NimBLEAdvertisedDevice::begin() const { + return m_payload.cbegin(); +} + +/** + * @brief Get the end iterator for the payload. + * @return A read only iterator pointing to one past the last byte of the payload. + */ +const std::vector::const_iterator NimBLEAdvertisedDevice::end() const { + return m_payload.cend(); +} + +#endif // CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_OBSERVER diff --git a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEAdvertisedDevice.h b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEAdvertisedDevice.h index 7869fb547..2348e8aee 100644 --- a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEAdvertisedDevice.h +++ b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEAdvertisedDevice.h @@ -1,36 +1,39 @@ /* - * NimBLEAdvertisedDevice.h + * Copyright 2020-2025 Ryan Powell and + * esp-nimble-cpp, NimBLE-Arduino contributors. * - * Created: on Jan 24 2020 - * Author H2zero + * 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 * - * Originally: + * http://www.apache.org/licenses/LICENSE-2.0 * - * BLEAdvertisedDevice.h - * - * Created on: Jul 3, 2017 - * Author: kolban + * 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 COMPONENTS_NIMBLEADVERTISEDDEVICE_H_ -#define COMPONENTS_NIMBLEADVERTISEDDEVICE_H_ +#ifndef NIMBLE_CPP_ADVERTISED_DEVICE_H_ +#define NIMBLE_CPP_ADVERTISED_DEVICE_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 "NimBLEScan.h" -#include "NimBLEUUID.h" +# include "NimBLEAddress.h" +# include "NimBLEScan.h" +# include "NimBLEUUID.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 - -#include -#include -#include +# if defined(CONFIG_NIMBLE_CPP_IDF) +# include "host/ble_hs_adv.h" +# include "host/ble_gap.h" +# else +# include "nimble/nimble/host/include/host/ble_hs_adv.h" +# include "nimble/nimble/host/include/host/ble_gap.h" +# endif +# include class NimBLEScan; /** @@ -40,20 +43,61 @@ class NimBLEScan; * class provides a model of a detected device. */ class NimBLEAdvertisedDevice { -public: - NimBLEAdvertisedDevice(); + public: + NimBLEAdvertisedDevice() = default; - NimBLEAddress getAddress(); - uint8_t getAdvType(); - uint8_t getAdvFlags(); - uint16_t getAppearance(); - uint16_t getAdvInterval(); - uint16_t getMinInterval(); - uint16_t getMaxInterval(); - uint8_t getManufacturerDataCount(); - std::string getManufacturerData(uint8_t index = 0); - std::string getURI(); - std::string getPayloadByType(uint16_t type); + uint8_t getAdvType() const; + uint8_t getAdvFlags() const; + uint16_t getAppearance() const; + uint16_t getAdvInterval() const; + uint16_t getMinInterval() const; + uint16_t getMaxInterval() const; + uint8_t getManufacturerDataCount() const; + const NimBLEAddress& getAddress() const; + std::string getManufacturerData(uint8_t index = 0) const; + std::string getURI() const; + 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& getPayload() const; + const std::vector::const_iterator begin() const; + const std::vector::const_iterator end() const; /** * @brief A template to convert the service data to . @@ -63,21 +107,14 @@ public: * less than sizeof(). * @details Use: getManufacturerData(skipSizeCheck); */ - template - T getManufacturerData(bool skipSizeCheck = false) { + template + T getManufacturerData(bool skipSizeCheck = false) const { std::string data = getManufacturerData(); - if(!skipSizeCheck && data.size() < sizeof(T)) return T(); - const char *pData = data.data(); - return *((T *)pData); + if (!skipSizeCheck && data.size() < sizeof(T)) return T(); + const char* pData = data.data(); + 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 . * @tparam T The type to convert the data to. @@ -87,12 +124,12 @@ public: * less than sizeof(). * @details Use: getServiceData(skipSizeCheck); */ - template - T getServiceData(uint8_t index = 0, bool skipSizeCheck = false) { + template + T getServiceData(uint8_t index = 0, bool skipSizeCheck = false) const { std::string data = getServiceData(index); - if(!skipSizeCheck && data.size() < sizeof(T)) return T(); - const char *pData = data.data(); - return *((T *)pData); + if (!skipSizeCheck && data.size() < sizeof(T)) return T(); + const char* pData = data.data(); + return *((T*)pData); } /** @@ -104,80 +141,38 @@ public: * less than sizeof(). * @details Use: getServiceData(skipSizeCheck); */ - template - T getServiceData(const NimBLEUUID &uuid, bool skipSizeCheck = false) { + template + T getServiceData(const NimBLEUUID& uuid, bool skipSizeCheck = false) const { std::string data = getServiceData(uuid); - if(!skipSizeCheck && data.size() < sizeof(T)) return T(); - const char *pData = data.data(); - return *((T *)pData); + if (!skipSizeCheck && data.size() < sizeof(T)) return T(); + const char* pData = data.data(); + return *((T*)pData); } - NimBLEUUID getServiceDataUUID(uint8_t index = 0); - NimBLEUUID getServiceUUID(uint8_t index = 0); - 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: + private: friend class NimBLEScan; - void setAddress(NimBLEAddress address); - void setAdvType(uint8_t advType, bool isLegacyAdv); - void setPayload(const uint8_t *payload, uint8_t length, bool append); - void setRSSI(int rssi); -#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); + NimBLEAdvertisedDevice(const ble_gap_event* event, uint8_t eventType); + void update(const ble_gap_event* event, uint8_t eventType); + uint8_t findAdvField(uint8_t type, uint8_t index = 0, size_t* data_loc = nullptr) const; + size_t findServiceData(uint8_t index, uint8_t* bytes) const; - NimBLEAddress m_address = NimBLEAddress(""); - uint8_t m_advType; - int m_rssi; - time_t m_timestamp; - uint8_t m_callbackSent; - 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 + NimBLEAddress m_address{}; + uint8_t m_advType{}; + int8_t m_rssi{}; + uint8_t m_callbackSent{}; + uint8_t m_advLength{}; - std::vector 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 m_payload; }; #endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_OBSERVER */ -#endif /* COMPONENTS_NIMBLEADVERTISEDDEVICE_H_ */ +#endif /* NIMBLE_CPP_ADVERTISED_DEVICE_H_ */ diff --git a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEAdvertisementData.cpp b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEAdvertisementData.cpp new file mode 100644 index 000000000..7d4c391af --- /dev/null +++ b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEAdvertisementData.cpp @@ -0,0 +1,586 @@ +/* + * Copyright 2020-2025 Ryan Powell 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& 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(minInterval, 0x6); + minInterval = std::min(minInterval, 0xC80); + maxInterval = std::max(maxInterval, 0x6); + maxInterval = std::min(maxInterval, 0xC80); + maxInterval = std::max(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(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& 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(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(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& 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& 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& 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& 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& 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(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& 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 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 NimBLEAdvertisementData::getPayload() const { + return m_payload; +} // getPayload + +/** + * @brief Clear the advertisement data for reuse. + */ +void NimBLEAdvertisementData::clearData() { + std::vector().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_) diff --git a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEAdvertisementData.h b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEAdvertisementData.h new file mode 100644 index 000000000..d10047445 --- /dev/null +++ b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEAdvertisementData.h @@ -0,0 +1,78 @@ +/* + * Copyright 2020-2025 Ryan Powell 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 +# include +# include + +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& 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& 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& uuids); + bool setCompleteServices32(const std::vector& uuids); + bool setPartialServices(const NimBLEUUID& uuid); + bool setPartialServices16(const std::vector& uuids); + bool setPartialServices32(const std::vector& 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& data); + bool removeData(uint8_t type); + void clearData(); + int getDataLocation(uint8_t type) const; + + std::string toString() const; + std::vector getPayload() const; + + private: + friend class NimBLEAdvertising; + + bool setServices(bool complete, uint8_t size, const std::vector& v_uuid); + std::vector 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_ diff --git a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEAdvertising.cpp b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEAdvertising.cpp index 0ce08ed58..f17d930d2 100644 --- a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEAdvertising.cpp +++ b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEAdvertising.cpp @@ -1,336 +1,153 @@ /* - * NimBLEAdvertising.cpp + * Copyright 2020-2025 Ryan Powell and + * esp-nimble-cpp, NimBLE-Arduino contributors. * - * Created: on March 3, 2020 - * Author H2zero + * 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 * - * Originally: - * - * BLEAdvertising.cpp - * - * This class encapsulates advertising a BLE Server. - * Created on: Jun 21, 2017 - * Author: kolban + * 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 "nimconfig.h" -#if (defined(CONFIG_BT_ENABLED) && \ - defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER) && \ - !CONFIG_BT_NIMBLE_EXT_ADV) || defined(_DOXYGEN_) -#if defined(CONFIG_NIMBLE_CPP_IDF) -#include "services/gap/ble_svc_gap.h" -#else -#include "nimble/nimble/host/services/gap/include/services/gap/ble_svc_gap.h" -#endif #include "NimBLEAdvertising.h" -#include "NimBLEDevice.h" -#include "NimBLEServer.h" -#include "NimBLEUtils.h" -#include "NimBLELog.h" +#if (CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_BROADCASTER && !CONFIG_BT_NIMBLE_EXT_ADV) || defined(_DOXYGEN_) + +# if defined(CONFIG_NIMBLE_CPP_IDF) +# include "services/gap/ble_svc_gap.h" +# else +# include "nimble/nimble/host/services/gap/include/services/gap/ble_svc_gap.h" +# endif +# include "NimBLEDevice.h" +# include "NimBLEServer.h" +# include "NimBLEUtils.h" +# include "NimBLELog.h" static const char* LOG_TAG = "NimBLEAdvertising"; - /** * @brief Construct a default advertising object. */ -NimBLEAdvertising::NimBLEAdvertising() { - reset(); +NimBLEAdvertising::NimBLEAdvertising() + : m_advData{}, + m_scanData{}, + m_advParams{}, + m_advCompCb{nullptr}, + m_slaveItvl{0}, + m_duration{BLE_HS_FOREVER}, + m_scanResp{false}, + m_advDataSet{false} { +# if !CONFIG_BT_NIMBLE_ROLE_PERIPHERAL + m_advParams.conn_mode = BLE_GAP_CONN_MODE_NON; +# else + m_advParams.conn_mode = BLE_GAP_CONN_MODE_UND; + m_advData.setFlags(BLE_HS_ADV_F_DISC_GEN); +# endif + m_advParams.disc_mode = BLE_GAP_DISC_MODE_GEN; } // NimBLEAdvertising - /** * @brief Stops the current advertising and resets the advertising data to the default values. + * @return True if successful. */ -void NimBLEAdvertising::reset() { - if(NimBLEDevice::getInitialized() && isAdvertising()) { - stop(); +bool NimBLEAdvertising::reset() { + if (!stop()) { + return false; } - memset(&m_advData, 0, sizeof m_advData); - memset(&m_scanData, 0, sizeof m_scanData); - memset(&m_advParams, 0, sizeof m_advParams); - memset(&m_slaveItvl, 0, sizeof m_slaveItvl); - const char *name = ble_svc_gap_device_name(); - m_advData.name = (uint8_t *)name; - m_advData.name_len = strlen(name); - m_advData.name_is_complete = 1; - m_advData.tx_pwr_lvl = NimBLEDevice::getPower(); - m_advData.flags = (BLE_HS_ADV_F_DISC_GEN | BLE_HS_ADV_F_BREDR_UNSUP); - -#if !defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) - m_advParams.conn_mode = BLE_GAP_CONN_MODE_NON; -#else - m_advParams.conn_mode = BLE_GAP_CONN_MODE_UND; -#endif - m_advParams.disc_mode = BLE_GAP_DISC_MODE_GEN; - m_customAdvData = false; - m_customScanResponseData = false; - m_scanResp = false; - m_advDataSet = false; - // Set this to non-zero to prevent auto start if host reset before started by app. - m_duration = BLE_HS_FOREVER; - m_advCompCB = nullptr; + *this = NimBLEAdvertising(); + return true; } // reset - /** - * @brief Add a service uuid to exposed list of services. - * @param [in] serviceUUID The UUID of the service to expose. - */ -void NimBLEAdvertising::addServiceUUID(const NimBLEUUID &serviceUUID) { - m_serviceUUIDs.push_back(serviceUUID); - m_advDataSet = false; -} // addServiceUUID - - -/** - * @brief Add a service uuid to exposed list of services. - * @param [in] serviceUUID The string representation of the service to expose. - */ -void NimBLEAdvertising::addServiceUUID(const char* serviceUUID) { - addServiceUUID(NimBLEUUID(serviceUUID)); - m_advDataSet = false; -} // addServiceUUID - - -/** - * @brief Remove a service UUID from the advertisment. - * @param [in] serviceUUID The UUID of the service to remove. - */ -void NimBLEAdvertising::removeServiceUUID(const NimBLEUUID &serviceUUID) { - for(auto it = m_serviceUUIDs.begin(); it != m_serviceUUIDs.end(); ++it) { - if((*it) == serviceUUID) { - m_serviceUUIDs.erase(it); - break; - } - } - m_advDataSet = false; -} // addServiceUUID - - -/** - * @brief Remove all service UUIDs from the advertisment. - */ -void NimBLEAdvertising::removeServices() { - std::vector().swap(m_serviceUUIDs); - m_advDataSet = false; -} // removeServices - - -/** - * @brief Set the device appearance in the advertising data. - * @param [in] appearance The appearance of the device in the advertising data. - */ -void NimBLEAdvertising::setAppearance(uint16_t appearance) { - m_advData.appearance = appearance; - m_advData.appearance_is_present = 1; - m_advDataSet = false; -} // setAppearance - - -/** - * @brief Add the transmission power level to the advertisement packet. - */ -void NimBLEAdvertising::addTxPower() { - m_advData.tx_pwr_lvl_is_present = 1; - m_advDataSet = false; -} // addTxPower - - -/** - * @brief Set the advertised name of the device. - * @param [in] name The name to advertise. - */ -void NimBLEAdvertising::setName(const std::string &name) { - std::vector(name.begin(), name.end()).swap(m_name); - m_advData.name = &m_name[0]; - m_advData.name_len = m_name.size(); - m_advDataSet = false; -} // setName - - -/** - * @brief Set the advertised manufacturer data. - * @param [in] data The data to advertise. - */ -void NimBLEAdvertising::setManufacturerData(const std::string &data) { - std::vector(data.begin(), data.end()).swap(m_mfgData); - m_advData.mfg_data = &m_mfgData[0]; - m_advData.mfg_data_len = m_mfgData.size(); - m_advDataSet = false; -} // setManufacturerData - - -/** - * @brief Set the advertised manufacturer data. - * @param [in] data The data to advertise. - */ -void NimBLEAdvertising::setManufacturerData(const std::vector &data) { - m_mfgData = data; - m_advData.mfg_data = &m_mfgData[0]; - m_advData.mfg_data_len = m_mfgData.size(); - m_advDataSet = false; -} // setManufacturerData - - -/** - * @brief Set the advertised URI. - * @param [in] uri The URI to advertise. - */ -void NimBLEAdvertising::setURI(const std::string &uri) { - std::vector(uri.begin(), uri.end()).swap(m_uri); - m_advData.uri = &m_uri[0]; - m_advData.uri_len = m_uri.size(); - m_advDataSet = false; -} // setURI - - -/** - * @brief Set the service data advertised for the UUID. - * @param [in] uuid The UUID the service data belongs to. - * @param [in] data The data to advertise. - * @note If data length is 0 the service data will not be advertised. - */ -void NimBLEAdvertising::setServiceData(const NimBLEUUID &uuid, const std::string &data) { - switch (uuid.bitSize()) { - case 16: { - std::vector((uint8_t*)&uuid.getNative()->u16.value, - (uint8_t*)&uuid.getNative()->u16.value + 2).swap(m_svcData16); - m_svcData16.insert(m_svcData16.end(), data.begin(), data.end()); - m_advData.svc_data_uuid16 = (uint8_t*)&m_svcData16[0]; - m_advData.svc_data_uuid16_len = (data.length() > 0) ? m_svcData16.size() : 0; - break; - } - - case 32: { - std::vector((uint8_t*)&uuid.getNative()->u32.value, - (uint8_t*)&uuid.getNative()->u32.value + 4).swap(m_svcData32); - m_svcData32.insert(m_svcData32.end(), data.begin(), data.end()); - m_advData.svc_data_uuid32 = (uint8_t*)&m_svcData32[0]; - m_advData.svc_data_uuid32_len = (data.length() > 0) ? m_svcData32.size() : 0; - break; - } - - case 128: { - std::vector(uuid.getNative()->u128.value, - uuid.getNative()->u128.value + 16).swap(m_svcData128); - m_svcData128.insert(m_svcData128.end(), data.begin(), data.end()); - m_advData.svc_data_uuid128 = (uint8_t*)&m_svcData128[0]; - m_advData.svc_data_uuid128_len = (data.length() > 0) ? m_svcData128.size() : 0; - break; - } - - default: - return; - } - - m_advDataSet = false; -} // setServiceData - - -/** - * @brief Set the type of advertisment to use. - * @param [in] adv_type: + * @brief Set the type of connectable mode to advertise. + * @param [in] mode The connectable mode: * * BLE_GAP_CONN_MODE_NON (0) - not connectable advertising * * BLE_GAP_CONN_MODE_DIR (1) - directed connectable advertising * * BLE_GAP_CONN_MODE_UND (2) - undirected connectable advertising + * @return True if the connectable mode was set, false if the mode is invalid. */ -void NimBLEAdvertising::setAdvertisementType(uint8_t adv_type){ - m_advParams.conn_mode = adv_type; +bool NimBLEAdvertising::setConnectableMode(uint8_t mode) { + if (mode > BLE_GAP_CONN_MODE_UND) { + NIMBLE_LOGE(LOG_TAG, "Invalid connectable mode: %u", mode); + return false; + } + + if (mode == BLE_GAP_CONN_MODE_NON) { // Non-connectable advertising doesn't need flags. + m_advData.setFlags(0); + } + + m_advParams.conn_mode = mode; + return true; } // setAdvertisementType +/** + * @brief Set the discoverable mode to use. + * @param [in] mode The discoverable mode: + * * BLE_GAP_DISC_MODE_NON (0) - non-discoverable + * * BLE_GAP_DISC_MODE_LTD (1) - limited discoverable + * * BLE_GAP_DISC_MODE_GEN (2) - general discoverable + * @return True if the discoverable mode was set, false if the mode is invalid. + */ +bool NimBLEAdvertising::setDiscoverableMode(uint8_t mode) { + switch (mode) { + case BLE_GAP_DISC_MODE_NON: + m_advData.setFlags(BLE_HS_ADV_F_BREDR_UNSUP); + break; + case BLE_GAP_DISC_MODE_LTD: + m_advData.setFlags(BLE_HS_ADV_F_DISC_LTD); + break; + case BLE_GAP_DISC_MODE_GEN: + m_advData.setFlags(BLE_HS_ADV_F_DISC_GEN); + break; + default: + NIMBLE_LOGE(LOG_TAG, "Invalid discoverable mode: %u", mode); + return false; + } + + m_advParams.disc_mode = mode; + return true; +} // setDiscoverableMode + +/** + * @brief Set the advertising interval. + * @param [in] interval The advertising interval in 0.625ms units, 0 = use default. + */ +void NimBLEAdvertising::setAdvertisingInterval(uint16_t interval) { + m_advParams.itvl_min = interval; + m_advParams.itvl_max = interval; +} // setAdvertisingInterval /** * @brief Set the minimum advertising interval. - * @param [in] mininterval Minimum value for advertising interval in 0.625ms units, 0 = use default. + * @param [in] minInterval Minimum value for advertising interval in 0.625ms units, 0 = use default. */ -void NimBLEAdvertising::setMinInterval(uint16_t mininterval) { - m_advParams.itvl_min = mininterval; +void NimBLEAdvertising::setMinInterval(uint16_t minInterval) { + m_advParams.itvl_min = minInterval; } // setMinInterval - /** * @brief Set the maximum advertising interval. - * @param [in] maxinterval Maximum value for advertising interval in 0.625ms units, 0 = use default. + * @param [in] maxInterval Maximum value for advertising interval in 0.625ms units, 0 = use default. */ -void NimBLEAdvertising::setMaxInterval(uint16_t maxinterval) { - m_advParams.itvl_max = maxinterval; +void NimBLEAdvertising::setMaxInterval(uint16_t maxInterval) { + m_advParams.itvl_max = maxInterval; } // setMaxInterval - /** - * @brief Set the advertised min connection interval preferred by this device. - * @param [in] mininterval the max interval value. Range = 0x0006 to 0x0C80. - * @details Values not within the range will cancel advertising of this data.\n - * Consumes 6 bytes of advertising space (combined with max interval). + * @brief Enable scan response data. + * @param [in] enable If true, scan response data will be available, false disabled, default = disabled. + * @details The scan response data is sent in response to a scan request from a peer device. */ -void NimBLEAdvertising::setMinPreferred(uint16_t mininterval) { - // invalid paramters, set the slave interval to null - if(mininterval < 0x0006 || mininterval > 0x0C80) { - m_advData.slave_itvl_range = nullptr; - return; - } - - if(m_advData.slave_itvl_range == nullptr) { - m_advData.slave_itvl_range = m_slaveItvl; - } - - m_slaveItvl[0] = mininterval; - m_slaveItvl[1] = mininterval >> 8; - - uint16_t maxinterval = *(uint16_t*)(m_advData.slave_itvl_range+2); - - // If mininterval is higher than the maxinterval make them the same - if(mininterval > maxinterval) { - m_slaveItvl[2] = m_slaveItvl[0]; - m_slaveItvl[3] = m_slaveItvl[1]; - } - +void NimBLEAdvertising::enableScanResponse(bool enable) { + m_scanResp = enable; m_advDataSet = false; -} // setMinPreferred - - -/** - * @brief Set the advertised max connection interval preferred by this device. - * @param [in] maxinterval the max interval value. Range = 0x0006 to 0x0C80. - * @details Values not within the range will cancel advertising of this data.\n - * Consumes 6 bytes of advertising space (combined with min interval). - */ -void NimBLEAdvertising::setMaxPreferred(uint16_t maxinterval) { - // invalid paramters, set the slave interval to null - if(maxinterval < 0x0006 || maxinterval > 0x0C80) { - m_advData.slave_itvl_range = nullptr; - return; - } - if(m_advData.slave_itvl_range == nullptr) { - m_advData.slave_itvl_range = m_slaveItvl; - } - m_slaveItvl[2] = maxinterval; - m_slaveItvl[3] = maxinterval >> 8; - - uint16_t mininterval = *(uint16_t*)(m_advData.slave_itvl_range); - - // If mininterval is higher than the maxinterval make them the same - if(mininterval > maxinterval) { - m_slaveItvl[0] = m_slaveItvl[2]; - m_slaveItvl[1] = m_slaveItvl[3]; - } - - m_advDataSet = false; -} // setMaxPreferred - - -/** - * @brief Set if scan response is available. - * @param [in] set true = scan response available. - */ -void NimBLEAdvertising::setScanResponse(bool set) { - m_scanResp = set; - m_advDataSet = false; -} // setScanResponse - +} // enableScanResponse /** * @brief Set the filtering for the scan filter. @@ -338,434 +155,148 @@ void NimBLEAdvertising::setScanResponse(bool set) { * @param [in] connectWhitelistOnly If true, only allow connections from those on the white list. */ void NimBLEAdvertising::setScanFilter(bool scanRequestWhitelistOnly, bool connectWhitelistOnly) { - NIMBLE_LOGD(LOG_TAG, ">> setScanFilter: scanRequestWhitelistOnly: %d, connectWhitelistOnly: %d", - scanRequestWhitelistOnly, connectWhitelistOnly); if (!scanRequestWhitelistOnly && !connectWhitelistOnly) { m_advParams.filter_policy = BLE_HCI_ADV_FILT_NONE; - NIMBLE_LOGD(LOG_TAG, "<< setScanFilter"); return; } if (scanRequestWhitelistOnly && !connectWhitelistOnly) { m_advParams.filter_policy = BLE_HCI_ADV_FILT_SCAN; - NIMBLE_LOGD(LOG_TAG, "<< setScanFilter"); return; } if (!scanRequestWhitelistOnly && connectWhitelistOnly) { m_advParams.filter_policy = BLE_HCI_ADV_FILT_CONN; - NIMBLE_LOGD(LOG_TAG, "<< setScanFilter"); return; } if (scanRequestWhitelistOnly && connectWhitelistOnly) { m_advParams.filter_policy = BLE_HCI_ADV_FILT_BOTH; - NIMBLE_LOGD(LOG_TAG, "<< setScanFilter"); return; } } // setScanFilter - -/** - * @brief Set the advertisement data that is to be published in a regular advertisement. - * @param [in] advertisementData The data to be advertised. - * @details The use of this function will replace any data set with addServiceUUID\n - * or setAppearance. If you wish for these to be advertised you must include them\n - * in the advertisementData parameter sent. - */ - -void NimBLEAdvertising::setAdvertisementData(NimBLEAdvertisementData& advertisementData) { - NIMBLE_LOGD(LOG_TAG, ">> setAdvertisementData"); - int rc = ble_gap_adv_set_data( - (uint8_t*)advertisementData.getPayload().data(), - advertisementData.getPayload().length()); - if (rc != 0) { - NIMBLE_LOGE(LOG_TAG, "ble_gap_adv_set_data: %d %s", - rc, NimBLEUtils::returnCodeToString(rc)); - } - m_customAdvData = true; // Set the flag that indicates we are using custom advertising data. - NIMBLE_LOGD(LOG_TAG, "<< setAdvertisementData"); -} // setAdvertisementData - - -/** - * @brief Set the advertisement data that is to be published in a scan response. - * @param [in] advertisementData The data to be advertised. - * @details Calling this without also using setAdvertisementData will have no effect.\n - * When using custom scan response data you must also use custom advertisement data. - */ -void NimBLEAdvertising::setScanResponseData(NimBLEAdvertisementData& advertisementData) { - NIMBLE_LOGD(LOG_TAG, ">> setScanResponseData"); - int rc = ble_gap_adv_rsp_set_data( - (uint8_t*)advertisementData.getPayload().data(), - advertisementData.getPayload().length()); - if (rc != 0) { - NIMBLE_LOGE(LOG_TAG, "ble_gap_adv_rsp_set_data: %d %s", - rc, NimBLEUtils::returnCodeToString(rc)); - } - m_customScanResponseData = true; // Set the flag that indicates we are using custom scan response data. - NIMBLE_LOGD(LOG_TAG, "<< setScanResponseData"); -} // setScanResponseData - - /** * @brief Start advertising. * @param [in] duration The duration, in milliseconds, to advertise, 0 == advertise forever. - * @param [in] advCompleteCB A pointer to a callback to be invoked when advertising ends. * @param [in] dirAddr The address of a peer to directly advertise to. * @return True if advertising started successfully. */ -bool NimBLEAdvertising::start(uint32_t duration, advCompleteCB_t advCompleteCB, NimBLEAddress* dirAddr) { - NIMBLE_LOGD(LOG_TAG, ">> Advertising start: customAdvData: %d, customScanResponseData: %d", - m_customAdvData, m_customScanResponseData); +bool NimBLEAdvertising::start(uint32_t duration, const NimBLEAddress* dirAddr) { + NIMBLE_LOGD(LOG_TAG, + ">> Advertising start: duration=%" PRIu32 ", dirAddr=%s", + duration, + dirAddr ? dirAddr->toString().c_str() : "NULL"); - // If Host is not synced we cannot start advertising. - if(!NimBLEDevice::m_synced) { - NIMBLE_LOGE(LOG_TAG, "Host reset, wait for sync."); + if (!NimBLEDevice::m_synced) { + NIMBLE_LOGE(LOG_TAG, "Host not synced!"); return false; } -#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) - NimBLEServer* pServer = NimBLEDevice::getServer(); - if(pServer != nullptr) { - if(!pServer->m_gattsStarted){ - pServer->start(); - } else if(pServer->getConnectedCount() >= NIMBLE_MAX_CONNECTIONS) { - NIMBLE_LOGE(LOG_TAG, "Max connections reached - not advertising"); - return false; - } - } -#endif - - // If already advertising just return - if(ble_gap_adv_active()) { + if (ble_gap_adv_active()) { NIMBLE_LOGW(LOG_TAG, "Advertising already active"); return true; } - // Save the duration incase of host reset so we can restart with the same params - m_duration = duration; - - if(duration == 0){ - duration = BLE_HS_FOREVER; +# if CONFIG_BT_NIMBLE_ROLE_PERIPHERAL + NimBLEServer* pServer = NimBLEDevice::getServer(); + if (pServer != nullptr) { + pServer->start(); // make sure the GATT server is ready before advertising } +# endif - m_advCompCB = advCompleteCB; - - m_advParams.disc_mode = BLE_GAP_DISC_MODE_GEN; - m_advData.flags = (BLE_HS_ADV_F_DISC_GEN | BLE_HS_ADV_F_BREDR_UNSUP); - if(m_advParams.conn_mode == BLE_GAP_CONN_MODE_NON) { - if(!m_scanResp) { - m_advParams.disc_mode = BLE_GAP_DISC_MODE_NON; - // non-connectable advertising does not require AD flags. - m_advData.flags = 0; - } - } - - int rc = 0; - - if (!m_customAdvData && !m_advDataSet) { - //start with 3 bytes for the flags data if required - uint8_t payloadLen = (m_advData.flags > 0) ? (2 + 1) : 0; - if(m_advData.mfg_data_len > 0) - payloadLen += (2 + m_advData.mfg_data_len); - - if(m_advData.svc_data_uuid16_len > 0) - payloadLen += (2 + m_advData.svc_data_uuid16_len); - - if(m_advData.svc_data_uuid32_len > 0) - payloadLen += (2 + m_advData.svc_data_uuid32_len); - - if(m_advData.svc_data_uuid128_len > 0) - payloadLen += (2 + m_advData.svc_data_uuid128_len); - - if(m_advData.uri_len > 0) - payloadLen += (2 + m_advData.uri_len); - - if(m_advData.appearance_is_present) - payloadLen += (2 + BLE_HS_ADV_APPEARANCE_LEN); - - if(m_advData.tx_pwr_lvl_is_present) - payloadLen += (2 + BLE_HS_ADV_TX_PWR_LVL_LEN); - - if(m_advData.slave_itvl_range != nullptr) - payloadLen += (2 + BLE_HS_ADV_SLAVE_ITVL_RANGE_LEN); - - for(auto &it : m_serviceUUIDs) { - if(it.getNative()->u.type == BLE_UUID_TYPE_16) { - int add = (m_advData.num_uuids16 > 0) ? 2 : 4; - if((payloadLen + add) > BLE_HS_ADV_MAX_SZ){ - m_advData.uuids16_is_complete = 0; - continue; - } - payloadLen += add; - - if(nullptr == (m_advData.uuids16 = (ble_uuid16_t*)realloc((void*)m_advData.uuids16, - (m_advData.num_uuids16 + 1) * sizeof(ble_uuid16_t)))) - { - NIMBLE_LOGE(LOG_TAG, "Error, no mem"); - return false; - } - memcpy((void*)&m_advData.uuids16[m_advData.num_uuids16], - &it.getNative()->u16, sizeof(ble_uuid16_t)); - m_advData.uuids16_is_complete = 1; - m_advData.num_uuids16++; - } - if(it.getNative()->u.type == BLE_UUID_TYPE_32) { - int add = (m_advData.num_uuids32 > 0) ? 4 : 6; - if((payloadLen + add) > BLE_HS_ADV_MAX_SZ){ - m_advData.uuids32_is_complete = 0; - continue; - } - payloadLen += add; - - if(nullptr == (m_advData.uuids32 = (ble_uuid32_t*)realloc((void*)m_advData.uuids32, - (m_advData.num_uuids32 + 1) * sizeof(ble_uuid32_t)))) - { - NIMBLE_LOGE(LOG_TAG, "Error, no mem"); - return false; - } - memcpy((void*)&m_advData.uuids32[m_advData.num_uuids32], - &it.getNative()->u32, sizeof(ble_uuid32_t)); - m_advData.uuids32_is_complete = 1; - m_advData.num_uuids32++; - } - if(it.getNative()->u.type == BLE_UUID_TYPE_128){ - int add = (m_advData.num_uuids128 > 0) ? 16 : 18; - if((payloadLen + add) > BLE_HS_ADV_MAX_SZ){ - m_advData.uuids128_is_complete = 0; - continue; - } - payloadLen += add; - - if(nullptr == (m_advData.uuids128 = (ble_uuid128_t*)realloc((void*)m_advData.uuids128, - (m_advData.num_uuids128 + 1) * sizeof(ble_uuid128_t)))) - { - NIMBLE_LOGE(LOG_TAG, "Error, no mem"); - return false; - } - memcpy((void*)&m_advData.uuids128[m_advData.num_uuids128], - &it.getNative()->u128, sizeof(ble_uuid128_t)); - m_advData.uuids128_is_complete = 1; - m_advData.num_uuids128++; - } - } - - // check if there is room for the name, if not put it in scan data - if((payloadLen + (2 + m_advData.name_len)) > BLE_HS_ADV_MAX_SZ) { - if(m_scanResp && !m_customScanResponseData){ - m_scanData.name = m_advData.name; - m_scanData.name_len = m_advData.name_len; - if(m_scanData.name_len > BLE_HS_ADV_MAX_SZ - 2) { - m_scanData.name_len = BLE_HS_ADV_MAX_SZ - 2; - m_scanData.name_is_complete = 0; - } else { - m_scanData.name_is_complete = 1; - } - m_advData.name = nullptr; - m_advData.name_len = 0; - m_advData.name_is_complete = 0; - } else { - if(m_advData.tx_pwr_lvl_is_present) { - m_advData.tx_pwr_lvl_is_present = 0; - payloadLen -= (2 + 1); - } - // if not using scan response just cut the name down - // leaving 2 bytes for the data specifier. - if(m_advData.name_len > (BLE_HS_ADV_MAX_SZ - payloadLen - 2)) { - m_advData.name_len = (BLE_HS_ADV_MAX_SZ - payloadLen - 2); - m_advData.name_is_complete = 0; - } - } - } - - if(m_scanResp && !m_customScanResponseData) { - rc = ble_gap_adv_rsp_set_fields(&m_scanData); - switch(rc) { - case 0: - break; - - case BLE_HS_EBUSY: - NIMBLE_LOGE(LOG_TAG, "Already advertising"); - break; - - case BLE_HS_EMSGSIZE: - NIMBLE_LOGE(LOG_TAG, "Scan data too long"); - break; - - default: - NIMBLE_LOGE(LOG_TAG, "Error setting scan response data; rc=%d, %s", - rc, NimBLEUtils::returnCodeToString(rc)); - break; - } - } - - if(rc == 0) { - rc = ble_gap_adv_set_fields(&m_advData); - switch(rc) { - case 0: - break; - - case BLE_HS_EBUSY: - NIMBLE_LOGE(LOG_TAG, "Already advertising"); - break; - - case BLE_HS_EMSGSIZE: - NIMBLE_LOGE(LOG_TAG, "Advertisement data too long"); - break; - - default: - NIMBLE_LOGE(LOG_TAG, "Error setting advertisement data; rc=%d, %s", - rc, NimBLEUtils::returnCodeToString(rc)); - break; - } - } - - if(m_advData.num_uuids128 > 0) { - free((void*)m_advData.uuids128); - m_advData.uuids128 = nullptr; - m_advData.num_uuids128 = 0; - } - - if(m_advData.num_uuids32 > 0) { - free((void*)m_advData.uuids32); - m_advData.uuids32 = nullptr; - m_advData.num_uuids32 = 0; - } - - if(m_advData.num_uuids16 > 0) { - free((void*)m_advData.uuids16); - m_advData.uuids16 = nullptr; - m_advData.num_uuids16 = 0; - } - - if(rc !=0) { + if (!m_advDataSet) { + if (!setAdvertisementData(m_advData)) { return false; } - m_advDataSet = true; + if (m_scanResp && m_scanData.getPayload().size() > 0) { + if (!setScanResponseData(m_scanData)) { + return false; + } + } } - ble_addr_t peerAddr; - if (dirAddr != nullptr) { - memcpy(&peerAddr.val, dirAddr->getNative(), 6); - peerAddr.type = dirAddr->getType(); + // Save the duration incase of host reset so we can restart with the same params + m_duration = duration; + if (duration == 0) { + duration = BLE_HS_FOREVER; } -#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) - rc = ble_gap_adv_start(NimBLEDevice::m_own_addr_type, - (dirAddr != nullptr) ? &peerAddr : NULL, - duration, - &m_advParams, - (pServer != nullptr) ? NimBLEServer::handleGapEvent : - NimBLEAdvertising::handleGapEvent, - (void*)this); -#else - rc = ble_gap_adv_start(NimBLEDevice::m_own_addr_type, - (dirAddr != nullptr) ? &peerAddr : NULL, - duration, - &m_advParams, - NimBLEAdvertising::handleGapEvent, - (void*)this); -#endif - switch(rc) { - case 0: - break; - - case BLE_HS_EALREADY: - NIMBLE_LOGI(LOG_TAG, "Advertisement Already active"); - break; - - case BLE_HS_EINVAL: - NIMBLE_LOGE(LOG_TAG, "Unable to advertise - Duration too long"); - break; - - case BLE_HS_EPREEMPTED: - NIMBLE_LOGE(LOG_TAG, "Unable to advertise - busy"); - break; - - case BLE_HS_ETIMEOUT_HCI: - case BLE_HS_EOS: - case BLE_HS_ECONTROLLER: - case BLE_HS_ENOTSYNCED: - NIMBLE_LOGE(LOG_TAG, "Unable to advertise - Host Reset"); - break; - - default: - NIMBLE_LOGE(LOG_TAG, "Error enabling advertising; rc=%d, %s", - rc, NimBLEUtils::returnCodeToString(rc)); - break; +# if CONFIG_BT_NIMBLE_ROLE_PERIPHERAL + int rc = ble_gap_adv_start(NimBLEDevice::m_ownAddrType, + (dirAddr != nullptr) ? dirAddr->getBase() : NULL, + duration, + &m_advParams, + (pServer != nullptr) ? NimBLEServer::handleGapEvent : NimBLEAdvertising::handleGapEvent, + this); +# else + int rc = + ble_gap_adv_start(NimBLEDevice::m_ownAddrType, NULL, duration, &m_advParams, NimBLEAdvertising::handleGapEvent, this); +# endif + if (rc != 0 && rc != BLE_HS_EALREADY) { + NIMBLE_LOGE(LOG_TAG, "Error enabling advertising; rc=%d, %s", rc, NimBLEUtils::returnCodeToString(rc)); + return false; } NIMBLE_LOGD(LOG_TAG, "<< Advertising start"); - return (rc == 0 || rc == BLE_HS_EALREADY); + return true; } // start - /** * @brief Stop advertising. * @return True if advertising stopped successfully. */ bool NimBLEAdvertising::stop() { - NIMBLE_LOGD(LOG_TAG, ">> stop"); - int rc = ble_gap_adv_stop(); if (rc != 0 && rc != BLE_HS_EALREADY) { - NIMBLE_LOGE(LOG_TAG, "ble_gap_adv_stop rc=%d %s", - rc, NimBLEUtils::returnCodeToString(rc)); + NIMBLE_LOGE(LOG_TAG, "ble_gap_adv_stop rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc)); return false; } - NIMBLE_LOGD(LOG_TAG, "<< stop"); return true; } // stop - /** - * @brief Handles the callback when advertising stops. + * @brief Set the callback to be invoked when advertising stops. + * @param [in] callback The callback to be invoked when advertising stops. */ -void NimBLEAdvertising::advCompleteCB() { - if(m_advCompCB != nullptr) { - m_advCompCB(this); - } -} // advCompleteCB - +void NimBLEAdvertising::setAdvertisingCompleteCallback(advCompleteCB_t callback) { + m_advCompCb = callback; +} // setAdvertisingCompleteCallback /** * @brief Check if currently advertising. - * @return true if advertising is active. + * @return True if advertising is active. */ bool NimBLEAdvertising::isAdvertising() { return ble_gap_adv_active(); } // isAdvertising - /* * Host reset seems to clear advertising data, * we need clear the flag so it reloads it. */ void NimBLEAdvertising::onHostSync() { - NIMBLE_LOGD(LOG_TAG, "Host re-synced"); - m_advDataSet = false; // If we were advertising forever, restart it now - if(m_duration == 0) { - start(m_duration, m_advCompCB); + if (m_duration == 0) { + start(m_duration); } else { - // Otherwise we should tell the app that advertising stopped. - advCompleteCB(); + // Otherwise we should tell the app that advertising stopped. + if (m_advCompCb != nullptr) { + m_advCompCb(this); + } } } // onHostSync - /** * @brief Handler for gap events when not using peripheral role. * @param [in] event the event data. * @param [in] arg pointer to the advertising instance. */ -/*STATIC*/ -int NimBLEAdvertising::handleGapEvent(struct ble_gap_event *event, void *arg) { - NimBLEAdvertising *pAdv = (NimBLEAdvertising*)arg; +int NimBLEAdvertising::handleGapEvent(struct ble_gap_event* event, void* arg) { + NimBLEAdvertising* pAdv = (NimBLEAdvertising*)arg; - if(event->type == BLE_GAP_EVENT_ADV_COMPLETE) { - switch(event->adv_complete.reason) { + if (event->type == BLE_GAP_EVENT_ADV_COMPLETE) { + switch (event->adv_complete.reason) { // Don't call the callback if host reset, we want to // preserve the active flag until re-sync to restart advertising. case BLE_HS_ETIMEOUT_HCI: @@ -778,310 +309,315 @@ int NimBLEAdvertising::handleGapEvent(struct ble_gap_event *event, void *arg) { default: break; } - pAdv->advCompleteCB(); + + if (pAdv->m_advCompCb != nullptr) { + pAdv->m_advCompCb(pAdv); + } } return 0; -} +} // handleGapEvent +/* -------------------------------------------------------------------------- */ +/* Advertisement Data */ +/* -------------------------------------------------------------------------- */ /** - * @brief Add data to the payload to be advertised. - * @param [in] data The data to be added to the payload. + * @brief Set the advertisement data that is to be broadcast in a regular advertisement. + * @param [in] data The data to be broadcast. + * @return True if the data was set successfully. */ -void NimBLEAdvertisementData::addData(const std::string &data) { - if ((m_payload.length() + data.length()) > BLE_HS_ADV_MAX_SZ) { - NIMBLE_LOGE(LOG_TAG, "Advertisement data length exceeded"); - return; +bool NimBLEAdvertising::setAdvertisementData(const NimBLEAdvertisementData& data) { + int rc = ble_gap_adv_set_data(&data.getPayload()[0], data.getPayload().size()); + if (rc != 0) { + NIMBLE_LOGE(LOG_TAG, "ble_gap_adv_set_data: %d %s", rc, NimBLEUtils::returnCodeToString(rc)); + return false; } - m_payload.append(data); -} // addData + NIMBLE_LOGD(LOG_TAG, "setAdvertisementData: %s", data.toString().c_str()); + m_advData = data; // make a copy in the member object in case this is custom. + m_advDataSet = true; // Set the flag that indicates the data was set already so we don't set it again. + return true; +} // setAdvertisementData /** - * @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. + * @brief Get the current advertisement data. + * @returns a reference to the current advertisement data. */ -void NimBLEAdvertisementData::addData(char * data, size_t length) { - addData(std::string(data, length)); -} // addData - +const NimBLEAdvertisementData& NimBLEAdvertising::getAdvertisementData() { + return m_advData; +} // getAdvertisementData /** - * @brief Set the appearance. - * @param [in] appearance The appearance code value. + * @brief Set the data that is to be provided in a scan response. + * @param [in] data The data to be provided in the scan response + * @return True if the data was set successfully. + * @details The scan response data is sent in response to a scan request from a peer device. + * If this is set without setting the advertisement data when advertising starts this may be overwritten. */ -void NimBLEAdvertisementData::setAppearance(uint16_t appearance) { - char cdata[2]; - cdata[0] = 3; - cdata[1] = BLE_HS_ADV_TYPE_APPEARANCE; // 0x19 - addData(std::string(cdata, 2) + std::string((char*) &appearance, 2)); +bool NimBLEAdvertising::setScanResponseData(const NimBLEAdvertisementData& data) { + int rc = ble_gap_adv_rsp_set_data(&data.getPayload()[0], data.getPayload().size()); + if (rc != 0) { + NIMBLE_LOGE(LOG_TAG, "ble_gap_adv_rsp_set_data: %d %s", rc, NimBLEUtils::returnCodeToString(rc)); + return false; + } + + NIMBLE_LOGD(LOG_TAG, "setScanResponseData: %s", data.toString().c_str()); + m_scanData = data; // copy the data into the member object in case this is custom. + return true; +} // setScanResponseData + +/** + * @brief Get the current scan response data. + * @returns a reference to the current scan response data. + */ +const NimBLEAdvertisementData& NimBLEAdvertising::getScanData() { + return m_advData; +} // getScanData + +/** + * @brief Clear the advertisement and scan response data and set the flags to BLE_HS_ADV_F_DISC_GEN. + */ +void NimBLEAdvertising::clearData() { + m_advData.clearData(); + m_advData.setFlags(BLE_HS_ADV_F_DISC_GEN); + m_scanData.clearData(); + m_advDataSet = false; +} // clearData + +/** + * @brief Refresh advertsing data dynamically without stop/start cycle. + * For instance allows refreshing manufacturer data dynamically. + * + * @return True if the data was set successfully. + * @details If scan response is enabled it will be refreshed as well. + */ +bool NimBLEAdvertising::refreshAdvertisingData() { + bool success = setAdvertisementData(m_advData); + if (m_scanResp) { + success = setScanResponseData(m_scanData); + } + + return success; +} // refreshAdvertisingData + +/** + * @brief Add a service uuid to exposed list of services. + * @param [in] serviceUUID The UUID of the service to expose. + * @return True if the service was added successfully. + */ +bool NimBLEAdvertising::addServiceUUID(const NimBLEUUID& serviceUUID) { + if (!m_advData.addServiceUUID(serviceUUID)) { + if (!m_scanData.addServiceUUID(serviceUUID)) { + return false; + } + } + + m_advDataSet = false; + 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 the service was added successfully. + */ +bool NimBLEAdvertising::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 the service was removed successfully. + */ +bool NimBLEAdvertising::removeServiceUUID(const NimBLEUUID& serviceUUID) { + bool success = m_advData.removeServiceUUID(serviceUUID); + success = m_scanData.removeServiceUUID(serviceUUID); + m_advDataSet = false; + return success; +} // removeServiceUUID + +/** + * @brief Remove a service UUID from the advertisement. + * @param [in] serviceUUID The UUID of the service to remove. + * @return True if the service was removed successfully. + */ +bool NimBLEAdvertising::removeServiceUUID(const char* serviceUUID) { + return removeServiceUUID(NimBLEUUID(serviceUUID)); +} // removeServiceUUID + +/** + * @brief Remove all service UUIDs from the advertisement. + * @return True if the services were removed successfully. + */ +bool NimBLEAdvertising::removeServices() { + bool success = m_advData.removeServices(); + success = m_advDataSet = m_scanData.removeServices(); + m_advDataSet = false; + return success; +} // removeServices + +/** + * @brief Set the device appearance in the advertising data. + * @param [in] appearance The appearance of the device in the advertising data. + * If the appearance value is 0 then the appearance will not be in the advertisement. + * @return True if the appearance was set successfully. + */ +bool NimBLEAdvertising::setAppearance(uint16_t appearance) { + if (!m_advData.setAppearance(appearance)) { + if (!m_scanData.setAppearance(appearance)) { + return false; + } + } + + m_advDataSet = false; + return true; } // 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 + * @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. + * @return True if the preferred connection interval was set successfully. + * @details Range = 0x0006(7.5ms) to 0x0C80(4000ms), values not within the range will be limited to this range. */ -void NimBLEAdvertisementData::setFlags(uint8_t flag) { - char cdata[3]; - cdata[0] = 2; - cdata[1] = BLE_HS_ADV_TYPE_FLAGS; // 0x01 - cdata[2] = flag | BLE_HS_ADV_F_BREDR_UNSUP; - addData(std::string(cdata, 3)); -} // setFlag - - -/** - * @brief Set manufacturer specific data. - * @param [in] data The manufacturer data to advertise. - */ -void NimBLEAdvertisementData::setManufacturerData(const std::string &data) { - char cdata[2]; - cdata[0] = data.length() + 1; - cdata[1] = BLE_HS_ADV_TYPE_MFG_DATA ; // 0xff - addData(std::string(cdata, 2) + data); -} // setManufacturerData - - -/** - * @brief Set manufacturer specific data. - * @param [in] data The manufacturer data to advertise. - */ -void NimBLEAdvertisementData::setManufacturerData(const std::vector &data) { - char cdata[2]; - cdata[0] = data.size() + 1; - cdata[1] = BLE_HS_ADV_TYPE_MFG_DATA ; // 0xff - addData(std::string(cdata, 2) + std::string((char*)&data[0], data.size())); -} // setManufacturerData - - -/** - * @brief Set the URI to advertise. - * @param [in] uri The uri to advertise. - */ -void NimBLEAdvertisementData::setURI(const std::string &uri) { - char cdata[2]; - cdata[0] = uri.length() + 1; - cdata[1] = BLE_HS_ADV_TYPE_URI; - addData(std::string(cdata, 2) + uri); -} // setURI - - -/** - * @brief Set the complete name of this device. - * @param [in] name The name to advertise. - */ -void NimBLEAdvertisementData::setName(const std::string &name) { - char cdata[2]; - cdata[0] = name.length() + 1; - cdata[1] = BLE_HS_ADV_TYPE_COMP_NAME; // 0x09 - addData(std::string(cdata, 2) + name); -} // setName - - -/** - * @brief Set a single service to advertise as a complete list of services. - * @param [in] uuid The service to advertise. - */ -void NimBLEAdvertisementData::setCompleteServices(const NimBLEUUID &uuid) { - setServices(true, uuid.bitSize(), {uuid}); -} // setCompleteServices - - -/** - * @brief Set the complete list of 16 bit services to advertise. - * @param [in] v_uuid A vector of 16 bit UUID's to advertise. - */ -void NimBLEAdvertisementData::setCompleteServices16(const std::vector& v_uuid) { - setServices(true, 16, v_uuid); -} // setCompleteServices16 - - -/** - * @brief Set the complete list of 32 bit services to advertise. - * @param [in] v_uuid A vector of 32 bit UUID's to advertise. - */ -void NimBLEAdvertisementData::setCompleteServices32(const std::vector& v_uuid) { - setServices(true, 32, v_uuid); -} // setCompleteServices32 - - -/** - * @brief Set a single service to advertise as a partial list of services. - * @param [in] uuid The service to advertise. - */ -void NimBLEAdvertisementData::setPartialServices(const NimBLEUUID &uuid) { - setServices(false, uuid.bitSize(), {uuid}); -} // setPartialServices - - -/** - * @brief Set the partial list of services to advertise. - * @param [in] v_uuid A vector of 16 bit UUID's to advertise. - */ -void NimBLEAdvertisementData::setPartialServices16(const std::vector& v_uuid) { - setServices(false, 16, v_uuid); -} // setPartialServices16 - - -/** - * @brief Set the partial list of services to advertise. - * @param [in] v_uuid A vector of 32 bit UUID's to advertise. - */ -void NimBLEAdvertisementData::setPartialServices32(const std::vector& v_uuid) { - setServices(false, 32, v_uuid); -} // setPartialServices32 - - -/** - * @brief Utility function to create the list of service UUID's from a vector. - * @param [in] complete If true the vector is the complete set of services. - * @param [in] size The bit size of the UUID's in the vector. (16, 32, or 128). - * @param [in] v_uuid The vector of service UUID's to advertise. - */ -void NimBLEAdvertisementData::setServices(const bool complete, const uint8_t size, - const std::vector &v_uuid) -{ - char cdata[2]; - cdata[0] = (size / 8) * v_uuid.size() + 1; - switch(size) { - case 16: - cdata[1] = complete ? BLE_HS_ADV_TYPE_COMP_UUIDS16 : BLE_HS_ADV_TYPE_INCOMP_UUIDS16; - break; - case 32: - cdata[1] = complete ? BLE_HS_ADV_TYPE_COMP_UUIDS32 : BLE_HS_ADV_TYPE_INCOMP_UUIDS32; - break; - case 128: - cdata[1] = complete ? BLE_HS_ADV_TYPE_COMP_UUIDS128 : BLE_HS_ADV_TYPE_INCOMP_UUIDS128; - break; - default: - return; - } - - std::string uuids; - - for(auto &it : v_uuid){ - if(it.bitSize() != size) { - NIMBLE_LOGE(LOG_TAG, "Service UUID(%d) invalid", size); - return; - } else { - switch(size) { - case 16: - uuids += std::string((char*)&it.getNative()->u16.value, 2); - break; - case 32: - uuids += std::string((char*)&it.getNative()->u32.value, 4); - break; - case 128: - uuids += std::string((char*)&it.getNative()->u128.value, 16); - break; - default: - return; - } +bool NimBLEAdvertising::setPreferredParams(uint16_t minInterval, uint16_t maxInterval) { + if (!m_advData.setPreferredParams(minInterval, maxInterval)) { + if (!m_scanData.setPreferredParams(minInterval, maxInterval)) { + return false; } } - addData(std::string(cdata, 2) + uuids); -} // setServices - - -/** - * @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. - */ -void NimBLEAdvertisementData::setServiceData(const NimBLEUUID &uuid, const std::string &data) { - char cdata[2]; - switch (uuid.bitSize()) { - case 16: { - // [Len] [0x16] [UUID16] data - cdata[0] = data.length() + 3; - cdata[1] = BLE_HS_ADV_TYPE_SVC_DATA_UUID16; // 0x16 - addData(std::string(cdata, 2) + std::string((char*) &uuid.getNative()->u16.value, 2) + data); - break; - } - - case 32: { - // [Len] [0x20] [UUID32] data - cdata[0] = data.length() + 5; - cdata[1] = BLE_HS_ADV_TYPE_SVC_DATA_UUID32; // 0x20 - addData(std::string(cdata, 2) + std::string((char*) &uuid.getNative()->u32.value, 4) + data); - break; - } - - case 128: { - // [Len] [0x21] [UUID128] data - cdata[0] = data.length() + 17; - cdata[1] = BLE_HS_ADV_TYPE_SVC_DATA_UUID128; // 0x21 - addData(std::string(cdata, 2) + std::string((char*) &uuid.getNative()->u128.value, 16) + data); - break; - } - - default: - return; - } -} // setServiceData - - -/** - * @brief Set the short name. - * @param [in] name The short name of the device. - */ -void NimBLEAdvertisementData::setShortName(const std::string &name) { - char cdata[2]; - cdata[0] = name.length() + 1; - cdata[1] = BLE_HS_ADV_TYPE_INCOMP_NAME; // 0x08 - addData(std::string(cdata, 2) + name); -} // setShortName - - -/** - * @brief Adds Tx power level to the advertisement data. - */ -void NimBLEAdvertisementData::addTxPower() { - char cdata[3]; - cdata[0] = BLE_HS_ADV_TX_PWR_LVL_LEN + 1; - cdata[1] = BLE_HS_ADV_TYPE_TX_PWR_LVL; - cdata[2] = NimBLEDevice::getPower(); - addData(cdata, 3); -} // addTxPower - - -/** - * @brief Set the preferred connection interval parameters. - * @param [in] min The minimum interval desired. - * @param [in] max The maximum interval desired. - */ -void NimBLEAdvertisementData::setPreferredParams(uint16_t min, uint16_t max) { - char cdata[6]; - cdata[0] = BLE_HS_ADV_SLAVE_ITVL_RANGE_LEN + 1; - cdata[1] = BLE_HS_ADV_TYPE_SLAVE_ITVL_RANGE; - cdata[2] = min; - cdata[3] = min >> 8; - cdata[4] = max; - cdata[5] = max >> 8; - addData(cdata, 6); + m_advDataSet = false; + return true; } // setPreferredParams +/** + * @brief Add the transmission power level to the advertisement packet. + * @return True if the transmission power level was added successfully. + */ +bool NimBLEAdvertising::addTxPower() { + if (!m_advData.addTxPower()) { + if (!m_scanData.addTxPower()) { + return false; + } + } + + m_advDataSet = false; + return true; +} // addTxPower /** - * @brief Retrieve the payload that is to be advertised. - * @return The payload that is to be advertised. + * @brief Set the advertised name of the device. + * @param [in] name The name to advertise. + * @return True if the name was set successfully. + * @note If the name is too long it will be truncated. + * @details If scan response is enabled the name will be set in the scan response data. */ -std::string NimBLEAdvertisementData::getPayload() { - return m_payload; -} // getPayload +bool NimBLEAdvertising::setName(const std::string& name) { + if (m_scanResp && m_scanData.setName(name)) { + m_advDataSet = false; + return true; + } + if (!m_advData.setName(name)) { + return false; + } + + m_advDataSet = false; + return true; +} // setName /** - * @brief Clear the advertisement data for reuse. + * @brief Set the advertised manufacturer data. + * @param [in] data The data to advertise. + * @param [in] length The length of the data. + * @return True if the manufacturer data was set successfully. */ -void NimBLEAdvertisementData::clearData() { - m_payload.clear(); -} +bool NimBLEAdvertising::setManufacturerData(const uint8_t* data, size_t length) { + if (!m_advData.setManufacturerData(data, length)) { + if (!m_scanData.setManufacturerData(data, length)) { + return false; + } + } -#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_BROADCASTER && !CONFIG_BT_NIMBLE_EXT_ADV */ + m_advDataSet = false; + return true; +} // setManufacturerData + +/** + * @brief Set the advertised manufacturer data. + * @param [in] data The data to advertise. + * @return True if the manufacturer data was set successfully. + */ +bool NimBLEAdvertising::setManufacturerData(const std::string& data) { + return setManufacturerData(reinterpret_cast(data.data()), data.length()); +} // setManufacturerData + +/** + * @brief Set the advertised manufacturer data. + * @param [in] data The data to advertise. + * @return True if the manufacturer data was set successfully. + */ +bool NimBLEAdvertising::setManufacturerData(const std::vector& data) { + return setManufacturerData(&data[0], data.size()); +} // setManufacturerData + +/** + * @brief Set the advertised URI. + * @param [in] uri The URI to advertise. + * @return True if the URI was set successfully. + */ +bool NimBLEAdvertising::setURI(const std::string& uri) { + if (!m_advData.setURI(uri)) { + if (!m_scanData.setURI(uri)) { + return false; + } + } + + m_advDataSet = false; + return true; +} // setURI + +/** + * @brief Set the service data advertised for the UUID. + * @param [in] uuid The UUID the service data belongs to. + * @param [in] data The data to advertise. + * @param [in] length The length of the data. + * @return True if the service data was set successfully. + * @note If data length is 0 the service data will not be advertised. + */ +bool NimBLEAdvertising::setServiceData(const NimBLEUUID& uuid, const uint8_t* data, size_t length) { + if (!m_advData.setServiceData(uuid, data, length)) { + if (!m_scanData.setServiceData(uuid, data, length)) { + return false; + } + } + + m_advDataSet = false; + return true; +} // 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 NimBLEAdvertising::setServiceData(const NimBLEUUID& uuid, const std::vector& data) { + return setServiceData(uuid, data.data(), data.size()); +} // 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 NimBLEAdvertising::setServiceData(const NimBLEUUID& uuid, const std::string& data) { + return setServiceData(uuid, reinterpret_cast(data.data()), data.length()); +} // setServiceData + +#endif // (CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_BROADCASTER && !CONFIG_BT_NIMBLE_EXT_ADV) || defined(_DOXYGEN_) diff --git a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEAdvertising.h b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEAdvertising.h index dc36d0732..b5a243cfe 100644 --- a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEAdvertising.h +++ b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEAdvertising.h @@ -1,150 +1,109 @@ /* - * NimBLEAdvertising.h + * Copyright 2020-2025 Ryan Powell and + * esp-nimble-cpp, NimBLE-Arduino contributors. * - * Created: on March 3, 2020 - * Author H2zero + * 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 * - * Originally: + * http://www.apache.org/licenses/LICENSE-2.0 * - * BLEAdvertising.h - * - * Created on: Jun 21, 2017 - * Author: kolban + * 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 MAIN_BLEADVERTISING_H_ -#define MAIN_BLEADVERTISING_H_ -#include "nimconfig.h" -#if (defined(CONFIG_BT_ENABLED) && \ - defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER) && \ - !CONFIG_BT_NIMBLE_EXT_ADV) || defined(_DOXYGEN_) +#ifndef NIMBLE_CPP_ADVERTISING_H_ +#define NIMBLE_CPP_ADVERTISING_H_ -#if defined(CONFIG_NIMBLE_CPP_IDF) -#include "host/ble_gap.h" -#else -#include "nimble/nimble/host/include/host/ble_gap.h" -#endif +#include "nimconfig.h" +#if (CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_BROADCASTER && !CONFIG_BT_NIMBLE_EXT_ADV) || defined(_DOXYGEN_) + +# if defined(CONFIG_NIMBLE_CPP_IDF) +# include "host/ble_gap.h" +# else +# include "nimble/nimble/host/include/host/ble_gap.h" +# endif /**** FIX COMPILATION ****/ -#undef min -#undef max +# undef min +# undef max /**************************/ -#include "NimBLEUUID.h" -#include "NimBLEAddress.h" +# include "NimBLEUUID.h" +# include "NimBLEAddress.h" +# include "NimBLEAdvertisementData.h" -#include -#include - -/* COMPATIBILITY - DO NOT USE */ -#define ESP_BLE_ADV_FLAG_LIMIT_DISC (0x01 << 0) -#define ESP_BLE_ADV_FLAG_GEN_DISC (0x01 << 1) -#define ESP_BLE_ADV_FLAG_BREDR_NOT_SPT (0x01 << 2) -#define ESP_BLE_ADV_FLAG_DMT_CONTROLLER_SPT (0x01 << 3) -#define ESP_BLE_ADV_FLAG_DMT_HOST_SPT (0x01 << 4) -#define ESP_BLE_ADV_FLAG_NON_LIMIT_DISC (0x00 ) - /* ************************* */ +# include +# include +# include class NimBLEAdvertising; - typedef std::function advCompleteCB_t; /** - * @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: - void setAppearance(uint16_t appearance); - void setCompleteServices(const NimBLEUUID &uuid); - void setCompleteServices16(const std::vector &v_uuid); - void setCompleteServices32(const std::vector &v_uuid); - void setFlags(uint8_t); - void setManufacturerData(const std::string &data); - void setManufacturerData(const std::vector &data); - void setURI(const std::string &uri); - void setName(const std::string &name); - void setPartialServices(const NimBLEUUID &uuid); - void setPartialServices16(const std::vector &v_uuid); - void setPartialServices32(const std::vector &v_uuid); - void setServiceData(const NimBLEUUID &uuid, const std::string &data); - void setShortName(const std::string &name); - void addData(const std::string &data); // Add data to the payload. - void addData(char * data, size_t length); - void addTxPower(); - void setPreferredParams(uint16_t min, uint16_t max); - std::string getPayload(); // Retrieve the current advert payload. - void clearData(); // Clear the advertisement data. - -private: - friend class NimBLEAdvertising; - void setServices(const bool complete, const uint8_t size, - const std::vector &v_uuid); - std::string m_payload; // The payload of the advertisement. -}; // NimBLEAdvertisementData - - -/** - * @brief Perform and manage %BLE advertising. + * @brief Perform and manage BLE advertising. * - * A %BLE server will want to perform advertising in order to make itself known to %BLE clients. + * A BLE server will want to perform advertising in order to make itself known to BLE clients. */ class NimBLEAdvertising { -public: + public: NimBLEAdvertising(); - void addServiceUUID(const NimBLEUUID &serviceUUID); - void addServiceUUID(const char* serviceUUID); - void removeServiceUUID(const NimBLEUUID &serviceUUID); - bool start(uint32_t duration = 0, advCompleteCB_t advCompleteCB = nullptr, NimBLEAddress* dirAddr = nullptr); - void removeServices(); + bool start(uint32_t duration = 0, const NimBLEAddress* dirAddr = nullptr); + void setAdvertisingCompleteCallback(advCompleteCB_t callback); bool stop(); - void setAppearance(uint16_t appearance); - void setName(const std::string &name); - void setManufacturerData(const std::string &data); - void setManufacturerData(const std::vector &data); - void setURI(const std::string &uri); - void setServiceData(const NimBLEUUID &uuid, const std::string &data); - void setAdvertisementType(uint8_t adv_type); - void setMaxInterval(uint16_t maxinterval); - void setMinInterval(uint16_t mininterval); - void setAdvertisementData(NimBLEAdvertisementData& advertisementData); - void setScanFilter(bool scanRequestWhitelistOnly, bool connectWhitelistOnly); - void setScanResponseData(NimBLEAdvertisementData& advertisementData); - void setScanResponse(bool); - void setMinPreferred(uint16_t); - void setMaxPreferred(uint16_t); - void addTxPower(); - void reset(); - void advCompleteCB(); + bool setConnectableMode(uint8_t mode); + bool setDiscoverableMode(uint8_t mode); + bool reset(); bool isAdvertising(); + void setScanFilter(bool scanRequestWhitelistOnly, bool connectWhitelistOnly); + void enableScanResponse(bool enable); + void setAdvertisingInterval(uint16_t interval); + void setMaxInterval(uint16_t maxInterval); + void setMinInterval(uint16_t minInterval); -private: + bool setAdvertisementData(const NimBLEAdvertisementData& advertisementData); + bool setScanResponseData(const NimBLEAdvertisementData& advertisementData); + const NimBLEAdvertisementData& getAdvertisementData(); + const NimBLEAdvertisementData& getScanData(); + void clearData(); + bool refreshAdvertisingData(); + + bool addServiceUUID(const NimBLEUUID& serviceUUID); + bool addServiceUUID(const char* serviceUUID); + bool removeServiceUUID(const NimBLEUUID& serviceUUID); + bool removeServiceUUID(const char* serviceUUID); + bool removeServices(); + bool setAppearance(uint16_t appearance); + bool setPreferredParams(uint16_t minInterval, uint16_t maxInterval); + bool addTxPower(); + bool setName(const std::string& name); + bool setManufacturerData(const uint8_t* data, size_t length); + bool setManufacturerData(const std::string& data); + bool setManufacturerData(const std::vector& data); + bool setURI(const std::string& uri); + 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& data); + + private: friend class NimBLEDevice; friend class NimBLEServer; - void onHostSync(); - static int handleGapEvent(struct ble_gap_event *event, void *arg); + void onHostSync(); + static int handleGapEvent(ble_gap_event* event, void* arg); - ble_hs_adv_fields m_advData; - ble_hs_adv_fields m_scanData; + NimBLEAdvertisementData m_advData; + NimBLEAdvertisementData m_scanData; ble_gap_adv_params m_advParams; - std::vector m_serviceUUIDs; - bool m_customAdvData; - bool m_customScanResponseData; - bool m_scanResp; - bool m_advDataSet; - advCompleteCB_t m_advCompCB{nullptr}; + advCompleteCB_t m_advCompCb; uint8_t m_slaveItvl[4]; uint32_t m_duration; - std::vector m_svcData16; - std::vector m_svcData32; - std::vector m_svcData128; - std::vector m_name; - std::vector m_mfgData; - std::vector m_uri; + bool m_scanResp : 1; + bool m_advDataSet : 1; }; -#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_BROADCASTER && !CONFIG_BT_NIMBLE_EXT_ADV */ -#endif /* MAIN_BLEADVERTISING_H_ */ +#endif // (CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_BROADCASTER && !CONFIG_BT_NIMBLE_EXT_ADV) || defined(_DOXYGEN_) +#endif // NIMBLE_CPP_ADVERTISING_H_ diff --git a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEAttValue.cpp b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEAttValue.cpp new file mode 100644 index 000000000..fbc164c9c --- /dev/null +++ b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEAttValue.cpp @@ -0,0 +1,162 @@ +/* + * Copyright 2020-2025 Ryan Powell 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 "NimBLEAttValue.h" +#if CONFIG_BT_ENABLED + +# if defined(CONFIG_NIMBLE_CPP_IDF) +# include "nimble/nimble_npl.h" +# else +# include "nimble/nimble/include/nimble/nimble_npl.h" +# endif + +# include "NimBLELog.h" + +static const char* LOG_TAG = "NimBLEAttValue"; + +// Default constructor implementation. +NimBLEAttValue::NimBLEAttValue(uint16_t init_len, uint16_t max_len) + : m_attr_value{static_cast(calloc(init_len + 1, 1))}, + m_attr_max_len{std::min(BLE_ATT_ATTR_MAX_LEN, max_len)}, + m_attr_len{}, + m_capacity{init_len} +# if CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED + , + m_timestamp{} +# endif +{ + NIMBLE_CPP_DEBUG_ASSERT(m_attr_value); + if (m_attr_value == nullptr) { + NIMBLE_LOGE(LOG_TAG, "Failed to calloc ctx"); + } +} + +// Value constructor implementation. +NimBLEAttValue::NimBLEAttValue(const uint8_t* value, uint16_t len, uint16_t max_len) : NimBLEAttValue(len, max_len) { + if (m_attr_value != nullptr) { + memcpy(m_attr_value, value, len); + m_attr_len = len; + } +} + +// Destructor implementation. +NimBLEAttValue::~NimBLEAttValue() { + if (m_attr_value != nullptr) { + free(m_attr_value); + } +} + +// Move assignment operator implementation. +NimBLEAttValue& NimBLEAttValue::operator=(NimBLEAttValue&& source) { + if (this != &source) { + free(m_attr_value); + m_attr_value = source.m_attr_value; + m_attr_max_len = source.m_attr_max_len; + m_attr_len = source.m_attr_len; + m_capacity = source.m_capacity; + setTimeStamp(source.getTimeStamp()); + source.m_attr_value = nullptr; + } + + return *this; +} + +// Copy assignment implementation. +NimBLEAttValue& NimBLEAttValue::operator=(const NimBLEAttValue& source) { + if (this != &source) { + deepCopy(source); + } + return *this; +} + +// Copy all the data from the source object to this object, including allocated space. +void NimBLEAttValue::deepCopy(const NimBLEAttValue& source) { + uint8_t* res = static_cast(realloc(m_attr_value, source.m_capacity + 1)); + NIMBLE_CPP_DEBUG_ASSERT(res); + if (res == nullptr) { + NIMBLE_LOGE(LOG_TAG, "Failed to realloc deepCopy"); + return; + } + + ble_npl_hw_enter_critical(); + m_attr_value = res; + m_attr_max_len = source.m_attr_max_len; + m_attr_len = source.m_attr_len; + m_capacity = source.m_capacity; + setTimeStamp(source.getTimeStamp()); + memcpy(m_attr_value, source.m_attr_value, m_attr_len + 1); + ble_npl_hw_exit_critical(0); +} + +// Set the value of the attribute. +bool NimBLEAttValue::setValue(const uint8_t* value, uint16_t len) { + m_attr_len = 0; // Just set the value length to 0 and append instead of repeating code. + m_attr_value[0] = '\0'; // Set the first byte to 0 incase the len of the new value is 0. + append(value, len); + return memcmp(m_attr_value, value, len) == 0 && m_attr_len == len; +} + +// Append the new data, allocate as necessary. +NimBLEAttValue& NimBLEAttValue::append(const uint8_t* value, uint16_t len) { + if (len == 0) { + return *this; + } + + if ((m_attr_len + len) > m_attr_max_len) { + NIMBLE_LOGE(LOG_TAG, "val > max, len=%u, max=%u", len, m_attr_max_len); + return *this; + } + + uint8_t* res = m_attr_value; + uint16_t new_len = m_attr_len + len; + if (new_len > m_capacity) { + res = static_cast(realloc(m_attr_value, (new_len + 1))); + m_capacity = new_len; + } + NIMBLE_CPP_DEBUG_ASSERT(res); + if (res == nullptr) { + NIMBLE_LOGE(LOG_TAG, "Failed to realloc append"); + return *this; + } + +# if CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED + time_t t = time(nullptr); +# else + time_t t = 0; +# endif + + ble_npl_hw_enter_critical(); + memcpy(res + m_attr_len, value, len); + m_attr_value = res; + m_attr_len = new_len; + m_attr_value[m_attr_len] = '\0'; + setTimeStamp(t); + ble_npl_hw_exit_critical(0); + + return *this; +} + +uint8_t NimBLEAttValue::operator[](int pos) const { + NIMBLE_CPP_DEBUG_ASSERT(pos < m_attr_len); + if (pos >= m_attr_len) { + NIMBLE_LOGE(LOG_TAG, "pos >= len, pos=%u, len=%u", pos, m_attr_len); + return 0; + } + return m_attr_value[pos]; +} + +#endif // CONFIG_BT_ENABLED diff --git a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEAttValue.h b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEAttValue.h index cc8599ab6..24b64c83c 100644 --- a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEAttValue.h +++ b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEAttValue.h @@ -1,55 +1,67 @@ /* - * NimBLEAttValue.h + * Copyright 2020-2025 Ryan Powell and + * esp-nimble-cpp, NimBLE-Arduino contributors. * - * Created: on March 18, 2021 - * Author H2zero + * 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 MAIN_NIMBLEATTVALUE_H_ -#define MAIN_NIMBLEATTVALUE_H_ +#ifndef NIMBLE_CPP_ATTVALUE_H +#define NIMBLE_CPP_ATTVALUE_H + #include "nimconfig.h" -#if defined(CONFIG_BT_ENABLED) +#if CONFIG_BT_ENABLED -#ifdef NIMBLE_CPP_ARDUINO_STRING_AVAILABLE -#include -#endif +# ifdef NIMBLE_CPP_ARDUINO_STRING_AVAILABLE +# include +# endif -#include "NimBLELog.h" +# include +# include +# include +# include +# include -/**** FIX COMPILATION ****/ -#undef min -#undef max -/**************************/ +# ifndef CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED +# define CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED 0 +# endif -#include -#include -#include +# ifndef BLE_ATT_ATTR_MAX_LEN +# define BLE_ATT_ATTR_MAX_LEN 512 +# endif -#ifndef CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED -# define CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED 0 -#endif +# if !defined(CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH) +# define CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH 20 +# elif CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH > BLE_ATT_ATTR_MAX_LEN +# error CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH cannot be larger than 512 (BLE_ATT_ATTR_MAX_LEN) +# elif CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH < 1 +# error CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH cannot be less than 1; Range = 1 : 512 +# endif -#if CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED -# include -#endif +/* Used to determine if the type passed to a template has a data() and size() method. */ +template +struct Has_data_size : std::false_type {}; -#if !defined(CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH) -# define CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH 20 -#elif CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH > BLE_ATT_ATTR_MAX_LEN -# error CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH cannot be larger than 512 (BLE_ATT_ATTR_MAX_LEN) -#elif CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH < 1 -# error CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH cannot be less than 1; Range = 1 : 512 -#endif +template +struct Has_data_size().data())), decltype(void(std::declval().size()))> + : std::true_type {}; /* Used to determine if the type passed to a template has a c_str() and length() method. */ template -struct Has_c_str_len : std::false_type {}; +struct Has_c_str_length : std::false_type {}; template -struct Has_c_str_len().c_str())), - decltype(void(std::declval().length()))> : std::true_type {}; - +struct Has_c_str_length().c_str())), decltype(void(std::declval().length()))> + : std::true_type {}; /** * @brief A specialized container class to hold BLE attribute values. @@ -57,25 +69,23 @@ struct Has_c_str_len().c_str())), * standard container types for value storage, while being convertible to\n * many different container classes. */ -class NimBLEAttValue -{ - uint8_t* m_attr_value = nullptr; - uint16_t m_attr_max_len = 0; - uint16_t m_attr_len = 0; - uint16_t m_capacity = 0; -#if CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED - time_t m_timestamp = 0; -#endif - void deepCopy(const NimBLEAttValue & source); +class NimBLEAttValue { + uint8_t* m_attr_value{}; + uint16_t m_attr_max_len{}; + uint16_t m_attr_len{}; + uint16_t m_capacity{}; +# if CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED + time_t m_timestamp{}; +# endif + void deepCopy(const NimBLEAttValue& source); -public: + public: /** * @brief Default constructor. * @param[in] init_len The initial size in bytes. * @param[in] max_len The max size in bytes that the value can be. */ - NimBLEAttValue(uint16_t init_len = CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH, - uint16_t max_len = BLE_ATT_ATTR_MAX_LEN); + NimBLEAttValue(uint16_t init_len = CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH, uint16_t max_len = BLE_ATT_ATTR_MAX_LEN); /** * @brief Construct with an initial value from a buffer. @@ -83,25 +93,23 @@ public: * @param[in] len The size in bytes of the value to set. * @param[in] max_len The max size in bytes that the value can be. */ - NimBLEAttValue(const uint8_t *value, uint16_t len, - uint16_t max_len = BLE_ATT_ATTR_MAX_LEN); - - /** - * @brief Construct with an initializer list. - * @param list An initializer list containing the initial value to set. - * @param[in] max_len The max size in bytes that the value can be. - */ - NimBLEAttValue(std::initializer_list list, - uint16_t max_len = BLE_ATT_ATTR_MAX_LEN) - :NimBLEAttValue(list.begin(), (uint16_t)list.size(), max_len){} + NimBLEAttValue(const uint8_t* value, uint16_t len, uint16_t max_len = BLE_ATT_ATTR_MAX_LEN); /** * @brief Construct with an initial value from a const char string. * @param value A pointer to the initial value to set. * @param[in] max_len The max size in bytes that the value can be. */ - NimBLEAttValue(const char *value, uint16_t max_len = BLE_ATT_ATTR_MAX_LEN) - :NimBLEAttValue((uint8_t*)value, (uint16_t)strlen(value), max_len){} + NimBLEAttValue(const char* value, uint16_t max_len = BLE_ATT_ATTR_MAX_LEN) + : NimBLEAttValue((uint8_t*)value, (uint16_t)strlen(value), max_len) {} + + /** + * @brief Construct with an initializer list. + * @param list An initializer list containing the initial value to set. + * @param[in] max_len The max size in bytes that the value can be. + */ + NimBLEAttValue(std::initializer_list list, uint16_t max_len = BLE_ATT_ATTR_MAX_LEN) + : NimBLEAttValue(list.begin(), list.size(), max_len) {} /** * @brief Construct with an initial value from a std::string. @@ -109,7 +117,7 @@ public: * @param[in] max_len The max size in bytes that the value can be. */ NimBLEAttValue(const std::string str, uint16_t max_len = BLE_ATT_ATTR_MAX_LEN) - :NimBLEAttValue((uint8_t*)str.data(), (uint16_t)str.length(), max_len){} + : NimBLEAttValue(reinterpret_cast(&str[0]), str.length(), max_len) {} /** * @brief Construct with an initial value from a std::vector. @@ -117,90 +125,99 @@ public: * @param[in] max_len The max size in bytes that the value can be. */ NimBLEAttValue(const std::vector vec, uint16_t max_len = BLE_ATT_ATTR_MAX_LEN) - :NimBLEAttValue(&vec[0], (uint16_t)vec.size(), max_len){} + : NimBLEAttValue(&vec[0], vec.size(), max_len) {} -#ifdef NIMBLE_CPP_ARDUINO_STRING_AVAILABLE +# ifdef NIMBLE_CPP_ARDUINO_STRING_AVAILABLE /** * @brief Construct with an initial value from an Arduino String. * @param str An Arduino String containing to the initial value to set. * @param[in] max_len The max size in bytes that the value can be. */ NimBLEAttValue(const String str, uint16_t max_len = BLE_ATT_ATTR_MAX_LEN) - :NimBLEAttValue((uint8_t*)str.c_str(), str.length(), max_len){} -#endif + : NimBLEAttValue(reinterpret_cast(str.c_str()), str.length(), max_len) {} +# endif /** @brief Copy constructor */ - NimBLEAttValue(const NimBLEAttValue & source) { deepCopy(source); } + NimBLEAttValue(const NimBLEAttValue& source) { deepCopy(source); } /** @brief Move constructor */ - NimBLEAttValue(NimBLEAttValue && source) { *this = std::move(source); } + NimBLEAttValue(NimBLEAttValue&& source) { *this = std::move(source); } /** @brief Destructor */ ~NimBLEAttValue(); /** @brief Returns the max size in bytes */ - uint16_t max_size() const { return m_attr_max_len; } + uint16_t max_size() const { return m_attr_max_len; } /** @brief Returns the currently allocated capacity in bytes */ - uint16_t capacity() const { return m_capacity; } + uint16_t capacity() const { return m_capacity; } /** @brief Returns the current length of the value in bytes */ - uint16_t length() const { return m_attr_len; } + uint16_t length() const { return m_attr_len; } /** @brief Returns the current size of the value in bytes */ - uint16_t size() const { return m_attr_len; } + uint16_t size() const { return m_attr_len; } /** @brief Returns a pointer to the internal buffer of the value */ - const uint8_t* data() const { return m_attr_value; } + const uint8_t* data() const { return m_attr_value; } /** @brief Returns a pointer to the internal buffer of the value as a const char* */ - const char* c_str() const { return (const char*)m_attr_value; } + const char* c_str() const { return reinterpret_cast(m_attr_value); } /** @brief Iterator begin */ - const uint8_t* begin() const { return m_attr_value; } + const uint8_t* begin() const { return m_attr_value; } /** @brief Iterator end */ - const uint8_t* end() const { return m_attr_value + m_attr_len; } + const uint8_t* end() const { return m_attr_value + m_attr_len; } -#if CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED +# if CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED /** @brief Returns a timestamp of when the value was last updated */ - time_t getTimeStamp() const { return m_timestamp; } + time_t getTimeStamp() const { return m_timestamp; } /** @brief Set the timestamp to the current time */ - void setTimeStamp() { m_timestamp = time(nullptr); } + void setTimeStamp() { m_timestamp = time(nullptr); } /** * @brief Set the timestamp to the specified time * @param[in] t The timestamp value to set */ - void setTimeStamp(time_t t) { m_timestamp = t; } -#else - time_t getTimeStamp() const { return 0; } - void setTimeStamp() { } - void setTimeStamp(time_t t) { } -#endif + void setTimeStamp(time_t t) { m_timestamp = t; } +# else + time_t getTimeStamp() const { return 0; } + void setTimeStamp() {} + void setTimeStamp(time_t t) {} +# endif /** * @brief Set the value from a buffer - * @param[in] value A ponter to a buffer containing the value. + * @param[in] value A pointer to a buffer containing the value. * @param[in] len The length of the value in bytes. * @returns True if successful. */ - bool setValue(const uint8_t *value, uint16_t len); + bool setValue(const uint8_t* value, uint16_t len); /** * @brief Set value to the value of const char*. - * @param [in] s A ponter to a const char value to set. + * @param [in] s A pointer to a const char value to set. + * @param [in] len The length of the value in bytes, defaults to strlen(s). */ - bool setValue(const char* s) { - return setValue((uint8_t*)s, (uint16_t)strlen(s)); } + bool setValue(const char* s, uint16_t len = 0) { + if (len == 0) { + len = strlen(s); + } + return setValue(reinterpret_cast(s), len); + } - /** - * @brief Get a pointer to the value buffer with timestamp. - * @param[in] timestamp A ponter to a time_t variable to store the timestamp. - * @returns A pointer to the internal value buffer. - */ - const uint8_t* getValue(time_t *timestamp); + const NimBLEAttValue& getValue(time_t* timestamp = nullptr) const { + if (timestamp != nullptr) { +# if CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED + *timestamp = m_timestamp; +# else + *timestamp = 0; +# endif + } + return *this; + } /** * @brief Append data to the value. @@ -208,24 +225,25 @@ public: * @param[in] len The length of the value to append in bytes. * @returns A reference to the appended NimBLEAttValue. */ - NimBLEAttValue& append(const uint8_t *value, uint16_t len); - + NimBLEAttValue& append(const uint8_t* value, uint16_t len); /*********************** Template Functions ************************/ +# if __cplusplus < 201703L /** * @brief Template to set value to the value of val. - * @param [in] s The value to set. - * @details Only used for types without a `c_str()` method. + * @param [in] v The value to set. + * @details Only used for types without a `c_str()` and `length()` or `data()` and `size()` method. + * size must be evaluatable by `sizeof()`. */ - template -#ifdef _DOXYGEN_ + template +# ifdef _DOXYGEN_ bool -#else - typename std::enable_if::value, bool>::type -#endif - setValue(const T &s) { - return setValue((uint8_t*)&s, sizeof(T)); +# else + typename std::enable_if::value && !Has_c_str_length::value && !Has_data_size::value, bool>::type +# endif + setValue(const T& v) { + return setValue(reinterpret_cast(&v), sizeof(T)); } /** @@ -233,16 +251,49 @@ public: * @param [in] s The value to set. * @details Only used if the has a `c_str()` method. */ - template -#ifdef _DOXYGEN_ + template +# ifdef _DOXYGEN_ bool -#else - typename std::enable_if::value, bool>::type -#endif - setValue(const T & s) { - return setValue((uint8_t*)s.c_str(), (uint16_t)s.length()); +# else + typename std::enable_if::value && !Has_data_size::value, bool>::type +# endif + setValue(const T& s) { + return setValue(reinterpret_cast(s.c_str()), s.length()); } + /** + * @brief Template to set value to the value of val. + * @param [in] v The value to set. + * @details Only used if the has a `data()` and `size()` method. + */ + template +# ifdef _DOXYGEN_ + bool +# else + typename std::enable_if::value, bool>::type +# endif + setValue(const T& v) { + return setValue(reinterpret_cast(v.data()), v.size()); + } + +# else + /** + * @brief Template to set value to the value of val. + * @param [in] s The value to set. + * @note This function is only available if the type T is not a pointer. + */ + template + typename std::enable_if::value, bool>::type setValue(const T& s) { + if constexpr (Has_data_size::value) { + return setValue(reinterpret_cast(s.data()), s.size()); + } else if constexpr (Has_c_str_length::value) { + return setValue(reinterpret_cast(s.c_str()), s.length()); + } else { + return setValue(reinterpret_cast(&s), sizeof(s)); + } + } +# endif + /** * @brief Template to return the value as a . * @tparam T The type to convert the data to. @@ -253,196 +304,64 @@ public: * less than sizeof(). * @details Use: getValue(×tamp, skipSizeCheck); */ - template - T getValue(time_t *timestamp = nullptr, bool skipSizeCheck = false) { - if(!skipSizeCheck && size() < sizeof(T)) { - return T(); - } - return *((T *)getValue(timestamp)); - } + template + T getValue(time_t* timestamp = nullptr, bool skipSizeCheck = false) const { + if (timestamp != nullptr) { +# if CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED + *timestamp = m_timestamp; +# else + *timestamp = 0; +# endif + } + if (!skipSizeCheck && size() < sizeof(T)) { + return T(); + } + return *(reinterpret_cast(m_attr_value)); + } /*********************** Operators ************************/ /** @brief Subscript operator */ - uint8_t operator [](int pos) const { - NIMBLE_CPP_DEBUG_ASSERT(pos < m_attr_len); - return m_attr_value[pos]; } + uint8_t operator[](int pos) const; /** @brief Operator; Get the value as a std::vector. */ - operator std::vector() const { - return std::vector(m_attr_value, m_attr_value + m_attr_len); } + operator std::vector() const { return std::vector(m_attr_value, m_attr_value + m_attr_len); } /** @brief Operator; Get the value as a std::string. */ - operator std::string() const { - return std::string((char*)m_attr_value, m_attr_len); } + operator std::string() const { return std::string(reinterpret_cast(m_attr_value), m_attr_len); } /** @brief Operator; Get the value as a const uint8_t*. */ operator const uint8_t*() const { return m_attr_value; } /** @brief Operator; Append another NimBLEAttValue. */ - NimBLEAttValue& operator +=(const NimBLEAttValue & source) { - return append(source.data(), source.size()); } + NimBLEAttValue& operator+=(const NimBLEAttValue& source) { return append(source.data(), source.size()); } /** @brief Operator; Set the value from a std::string source. */ - NimBLEAttValue& operator =(const std::string & source) { - setValue((uint8_t*)source.data(), (uint16_t)source.size()); return *this; } + NimBLEAttValue& operator=(const std::string& source) { + setValue(reinterpret_cast(&source[0]), source.size()); + return *this; + } /** @brief Move assignment operator */ - NimBLEAttValue& operator =(NimBLEAttValue && source); + NimBLEAttValue& operator=(NimBLEAttValue&& source); /** @brief Copy assignment operator */ - NimBLEAttValue& operator =(const NimBLEAttValue & source); + NimBLEAttValue& operator=(const NimBLEAttValue& source); /** @brief Equality operator */ - bool operator ==(const NimBLEAttValue & source) { - return (m_attr_len == source.size()) ? - memcmp(m_attr_value, source.data(), m_attr_len) == 0 : false; } + bool operator==(const NimBLEAttValue& source) const { + return (m_attr_len == source.size()) ? memcmp(m_attr_value, source.data(), m_attr_len) == 0 : false; + } /** @brief Inequality operator */ - bool operator !=(const NimBLEAttValue & source){ return !(*this == source); } + bool operator!=(const NimBLEAttValue& source) const { return !(*this == source); } -#ifdef NIMBLE_CPP_ARDUINO_STRING_AVAILABLE +# ifdef NIMBLE_CPP_ARDUINO_STRING_AVAILABLE /** @brief Operator; Get the value as an Arduino String value. */ - operator String() const { return String((char*)m_attr_value); } -#endif - + operator String() const { return String(reinterpret_cast(m_attr_value)); } +# endif }; - -inline NimBLEAttValue::NimBLEAttValue(uint16_t init_len, uint16_t max_len) { - m_attr_value = (uint8_t*)calloc(init_len + 1, 1); - NIMBLE_CPP_DEBUG_ASSERT(m_attr_value); - m_attr_max_len = std::min(BLE_ATT_ATTR_MAX_LEN, (int)max_len); - m_attr_len = 0; - m_capacity = init_len; - setTimeStamp(0); -} - -inline NimBLEAttValue::NimBLEAttValue(const uint8_t *value, uint16_t len, uint16_t max_len) -: NimBLEAttValue(len, max_len) { - memcpy(m_attr_value, value, len); - m_attr_value[len] = '\0'; - m_attr_len = len; -} - -inline NimBLEAttValue::~NimBLEAttValue() { - if(m_attr_value != nullptr) { - free(m_attr_value); - } -} - -inline NimBLEAttValue& NimBLEAttValue::operator =(NimBLEAttValue && source) { - if (this != &source){ - free(m_attr_value); - - m_attr_value = source.m_attr_value; - m_attr_max_len = source.m_attr_max_len; - m_attr_len = source.m_attr_len; - m_capacity = source.m_capacity; - setTimeStamp(source.getTimeStamp()); - source.m_attr_value = nullptr; - } - return *this; -} - -inline NimBLEAttValue& NimBLEAttValue::operator =(const NimBLEAttValue & source) { - if (this != &source) { - deepCopy(source); - } - return *this; -} - -inline void NimBLEAttValue::deepCopy(const NimBLEAttValue & source) { - uint8_t* res = (uint8_t*)realloc( m_attr_value, source.m_capacity + 1); - NIMBLE_CPP_DEBUG_ASSERT(res); - - ble_npl_hw_enter_critical(); - m_attr_value = res; - m_attr_max_len = source.m_attr_max_len; - m_attr_len = source.m_attr_len; - m_capacity = source.m_capacity; - setTimeStamp(source.getTimeStamp()); - memcpy(m_attr_value, source.m_attr_value, m_attr_len + 1); - ble_npl_hw_exit_critical(0); -} - -inline const uint8_t* NimBLEAttValue::getValue(time_t *timestamp) { - if(timestamp != nullptr) { -#if CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED - *timestamp = m_timestamp; -#else - *timestamp = 0; -#endif - } - return m_attr_value; -} - -inline bool NimBLEAttValue::setValue(const uint8_t *value, uint16_t len) { - if (len > m_attr_max_len) { - NIMBLE_LOGE("NimBLEAttValue", "value exceeds max, len=%u, max=%u", - len, m_attr_max_len); - return false; - } - - uint8_t *res = m_attr_value; - if (len > m_capacity) { - res = (uint8_t*)realloc(m_attr_value, (len + 1)); - m_capacity = len; - } - NIMBLE_CPP_DEBUG_ASSERT(res); - -#if CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED - time_t t = time(nullptr); -#else - time_t t = 0; -#endif - - ble_npl_hw_enter_critical(); - m_attr_value = res; - memcpy(m_attr_value, value, len); - m_attr_value[len] = '\0'; - m_attr_len = len; - setTimeStamp(t); - ble_npl_hw_exit_critical(0); - return true; -} - -inline NimBLEAttValue& NimBLEAttValue::append(const uint8_t *value, uint16_t len) { - if (len < 1) { - return *this; - } - - if ((m_attr_len + len) > m_attr_max_len) { - NIMBLE_LOGE("NimBLEAttValue", "val > max, len=%u, max=%u", - len, m_attr_max_len); - return *this; - } - - uint8_t* res = m_attr_value; - uint16_t new_len = m_attr_len + len; - if (new_len > m_capacity) { - res = (uint8_t*)realloc(m_attr_value, (new_len + 1)); - m_capacity = new_len; - } - NIMBLE_CPP_DEBUG_ASSERT(res); - -#if CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED - time_t t = time(nullptr); -#else - time_t t = 0; -#endif - - ble_npl_hw_enter_critical(); - m_attr_value = res; - memcpy(m_attr_value + m_attr_len, value, len); - m_attr_len = new_len; - m_attr_value[m_attr_len] = '\0'; - setTimeStamp(t); - ble_npl_hw_exit_critical(0); - - return *this; -} - -#endif /*(CONFIG_BT_ENABLED) */ -#endif /* MAIN_NIMBLEATTVALUE_H_ */ +#endif // CONFIG_BT_ENABLED +#endif // NIMBLE_CPP_ATTVALUE_H_ diff --git a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEAttribute.h b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEAttribute.h new file mode 100644 index 000000000..7ba95c7d8 --- /dev/null +++ b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEAttribute.h @@ -0,0 +1,60 @@ +/* + * Copyright 2020-2025 Ryan Powell 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_ATTRIBUTE_H_ +#define NIMBLE_CPP_ATTRIBUTE_H_ + +#include "nimconfig.h" +#if CONFIG_BT_ENABLED && (CONFIG_BT_NIMBLE_ROLE_PERIPHERAL || CONFIG_BT_NIMBLE_ROLE_CENTRAL) + +# include "NimBLEUUID.h" + +/** + * @brief A base class for BLE attributes. + */ +class NimBLEAttribute { + public: + /** + * @brief Get the UUID of the attribute. + * @return The UUID. + */ + const NimBLEUUID& getUUID() const { return m_uuid; } + + /** + * @brief Get the handle of the attribute. + */ + uint16_t getHandle() const { return m_handle; }; + + protected: + /** + * @brief Construct a new NimBLEAttribute object. + * @param [in] handle The handle of the attribute. + * @param [in] uuid The UUID of the attribute. + */ + NimBLEAttribute(const NimBLEUUID& uuid, uint16_t handle) : m_uuid{uuid}, m_handle{handle} {} + + /** + * @brief Destroy the NimBLEAttribute object. + */ + ~NimBLEAttribute() = default; + + const NimBLEUUID m_uuid{}; + uint16_t m_handle{0}; +}; + +#endif // CONFIG_BT_ENABLED && (CONFIG_BT_NIMBLE_ROLE_PERIPHERAL || CONFIG_BT_NIMBLE_ROLE_CENTRAL) +#endif // NIMBLE_CPP_ATTRIBUTE_H_ diff --git a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEBeacon.cpp b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEBeacon.cpp index df24ced93..e9b936177 100644 --- a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEBeacon.cpp +++ b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEBeacon.cpp @@ -1,60 +1,45 @@ /* - * NimBLEBeacon2.cpp + * Copyright 2020-2025 Ryan Powell and + * esp-nimble-cpp, NimBLE-Arduino contributors. * - * Created: on March 15 2020 - * Author H2zero + * 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 * - * Originally: + * http://www.apache.org/licenses/LICENSE-2.0 * - * BLEBeacon.cpp - * - * Created on: Jan 4, 2018 - * Author: kolban + * 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 "nimconfig.h" -#if defined(CONFIG_BT_ENABLED) -#include -#include #include "NimBLEBeacon.h" -#include "NimBLELog.h" +#if CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_BROADCASTER -#define ENDIAN_CHANGE_U16(x) ((((x)&0xFF00)>>8) + (((x)&0xFF)<<8)) +# include "NimBLEUUID.h" +# include "NimBLELog.h" + +# define ENDIAN_CHANGE_U16(x) ((((x) & 0xFF00) >> 8) + (((x) & 0xFF) << 8)) static const char* LOG_TAG = "NimBLEBeacon"; - -/** - * @brief Construct a default beacon object. - */ -NimBLEBeacon::NimBLEBeacon() { - m_beaconData.manufacturerId = 0x4c00; - m_beaconData.subType = 0x02; - m_beaconData.subTypeLength = 0x15; - m_beaconData.major = 0; - m_beaconData.minor = 0; - m_beaconData.signalPower = 0; - memset(m_beaconData.proximityUUID, 0, sizeof(m_beaconData.proximityUUID)); -} // NimBLEBeacon - - /** * @brief Retrieve the data that is being advertised. * @return The advertised data. */ -std::string NimBLEBeacon::getData() { - return std::string((char*) &m_beaconData, sizeof(m_beaconData)); +const NimBLEBeacon::BeaconData& NimBLEBeacon::getData() { + return m_beaconData; } // getData - /** * @brief Get the major value being advertised. * @return The major value advertised. */ uint16_t NimBLEBeacon::getMajor() { return m_beaconData.major; -} - +} // getMajor /** * @brief Get the manufacturer ID being advertised. @@ -62,8 +47,7 @@ uint16_t NimBLEBeacon::getMajor() { */ uint16_t NimBLEBeacon::getManufacturerId() { return m_beaconData.manufacturerId; -} - +} // getManufacturerId /** * @brief Get the minor value being advertised. @@ -71,17 +55,15 @@ uint16_t NimBLEBeacon::getManufacturerId() { */ uint16_t NimBLEBeacon::getMinor() { return m_beaconData.minor; -} - +} // getMinor /** * @brief Get the proximity UUID being advertised. * @return The UUID advertised. */ NimBLEUUID NimBLEBeacon::getProximityUUID() { - return NimBLEUUID(m_beaconData.proximityUUID, 16, true); -} - + return NimBLEUUID(m_beaconData.proximityUUID, 16).reverseByteOrder(); +} // getProximityUUID /** * @brief Get the signal power being advertised. @@ -89,22 +71,28 @@ NimBLEUUID NimBLEBeacon::getProximityUUID() { */ int8_t NimBLEBeacon::getSignalPower() { return m_beaconData.signalPower; -} - +} // getSignalPower /** - * @brief Set the raw data for the beacon record. - * @param [in] data The raw beacon data. + * @brief Set the beacon data. + * @param [in] data A pointer to the raw data that the beacon should advertise. + * @param [in] length The length of the data. */ -void NimBLEBeacon::setData(const std::string &data) { - if (data.length() != sizeof(m_beaconData)) { - NIMBLE_LOGE(LOG_TAG, "Unable to set the data ... length passed in was %d and expected %d", - data.length(), sizeof(m_beaconData)); +void NimBLEBeacon::setData(const uint8_t* data, uint8_t length) { + if (length != sizeof(BeaconData)) { + NIMBLE_LOGE(LOG_TAG, "Data length must be %d bytes, sent: %d", sizeof(BeaconData), length); return; } - memcpy(&m_beaconData, data.data(), sizeof(m_beaconData)); + memcpy(&m_beaconData, data, length); } // setData +/** + * @brief Set the beacon data. + * @param [in] data The data that the beacon should advertise. + */ +void NimBLEBeacon::setData(const NimBLEBeacon::BeaconData& data) { + m_beaconData = data; +} // setData /** * @brief Set the major value. @@ -114,7 +102,6 @@ void NimBLEBeacon::setMajor(uint16_t major) { m_beaconData.major = ENDIAN_CHANGE_U16(major); } // setMajor - /** * @brief Set the manufacturer ID. * @param [in] manufacturerId The manufacturer ID value. @@ -123,7 +110,6 @@ void NimBLEBeacon::setManufacturerId(uint16_t manufacturerId) { m_beaconData.manufacturerId = ENDIAN_CHANGE_U16(manufacturerId); } // setManufacturerId - /** * @brief Set the minor value. * @param [in] minor The minor value. @@ -132,20 +118,17 @@ void NimBLEBeacon::setMinor(uint16_t minor) { m_beaconData.minor = ENDIAN_CHANGE_U16(minor); } // setMinor - /** * @brief Set the proximity UUID. * @param [in] uuid The proximity UUID. */ -void NimBLEBeacon::setProximityUUID(const NimBLEUUID &uuid) { +void NimBLEBeacon::setProximityUUID(const NimBLEUUID& uuid) { NimBLEUUID temp_uuid = uuid; temp_uuid.to128(); - std::reverse_copy(temp_uuid.getNative()->u128.value, - temp_uuid.getNative()->u128.value + 16, - m_beaconData.proximityUUID); + temp_uuid.reverseByteOrder(); + memcpy(m_beaconData.proximityUUID, temp_uuid.getValue(), 16); } // setProximityUUID - /** * @brief Set the signal power. * @param [in] signalPower The signal power value. @@ -154,4 +137,4 @@ void NimBLEBeacon::setSignalPower(int8_t signalPower) { m_beaconData.signalPower = signalPower; } // setSignalPower -#endif +#endif // CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_BROADCASTER diff --git a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEBeacon.h b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEBeacon.h index 82ee61c53..e624ef86b 100644 --- a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEBeacon.h +++ b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEBeacon.h @@ -1,51 +1,64 @@ /* - * NimBLEBeacon2.h + * Copyright 2020-2025 Ryan Powell and + * esp-nimble-cpp, NimBLE-Arduino contributors. * - * Created: on March 15 2020 - * Author H2zero + * 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 * - * Originally: + * http://www.apache.org/licenses/LICENSE-2.0 * - * BLEBeacon2.h - * - * Created on: Jan 4, 2018 - * Author: kolban + * 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 MAIN_NIMBLEBEACON_H_ -#define MAIN_NIMBLEBEACON_H_ +#ifndef NIMBLE_CPP_BEACON_H_ +#define NIMBLE_CPP_BEACON_H_ + +#include "nimconfig.h" +#if CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_BROADCASTER + +class NimBLEUUID; + +# include -#include "NimBLEUUID.h" /** * @brief Representation of a beacon. * See: * * https://en.wikipedia.org/wiki/IBeacon */ class NimBLEBeacon { -private: - struct { - uint16_t manufacturerId; - uint8_t subType; - uint8_t subTypeLength; - uint8_t proximityUUID[16]; - uint16_t major; - uint16_t minor; - int8_t signalPower; - } __attribute__((packed)) m_beaconData; -public: - NimBLEBeacon(); - std::string getData(); - uint16_t getMajor(); - uint16_t getMinor(); - uint16_t getManufacturerId(); - NimBLEUUID getProximityUUID(); - int8_t getSignalPower(); - void setData(const std::string &data); - void setMajor(uint16_t major); - void setMinor(uint16_t minor); - void setManufacturerId(uint16_t manufacturerId); - void setProximityUUID(const NimBLEUUID &uuid); - void setSignalPower(int8_t signalPower); + public: + struct BeaconData { + uint16_t manufacturerId{0x4c00}; + uint8_t subType{0x02}; + uint8_t subTypeLength{0x15}; + uint8_t proximityUUID[16]{}; + uint16_t major{}; + uint16_t minor{}; + int8_t signalPower{}; + } __attribute__((packed)); + + const BeaconData& getData(); + uint16_t getMajor(); + uint16_t getMinor(); + uint16_t getManufacturerId(); + NimBLEUUID getProximityUUID(); + int8_t getSignalPower(); + void setData(const uint8_t* data, uint8_t length); + void setData(const BeaconData& data); + void setMajor(uint16_t major); + void setMinor(uint16_t minor); + void setManufacturerId(uint16_t manufacturerId); + void setProximityUUID(const NimBLEUUID& uuid); + void setSignalPower(int8_t signalPower); + + private: + BeaconData m_beaconData; }; // NimBLEBeacon -#endif /* MAIN_NIMBLEBEACON_H_ */ +#endif // CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL +#endif // NIMBLE_CPP_BEACON_H_ diff --git a/lib/libesp32_div/esp-nimble-cpp/src/NimBLECharacteristic.cpp b/lib/libesp32_div/esp-nimble-cpp/src/NimBLECharacteristic.cpp index 82aa20fd1..26ce838eb 100644 --- a/lib/libesp32_div/esp-nimble-cpp/src/NimBLECharacteristic.cpp +++ b/lib/libesp32_div/esp-nimble-cpp/src/NimBLECharacteristic.cpp @@ -1,143 +1,148 @@ /* - * NimBLECharacteristic.cpp + * Copyright 2020-2025 Ryan Powell and + * esp-nimble-cpp, NimBLE-Arduino contributors. * - * Created: on March 3, 2020 - * Author H2zero + * 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 * - * BLECharacteristic.cpp + * http://www.apache.org/licenses/LICENSE-2.0 * - * Created on: Jun 22, 2017 - * Author: kolban + * 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 "nimconfig.h" -#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) +# include "NimBLECharacteristic.h" +#if CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL -#include "NimBLECharacteristic.h" -#include "NimBLE2904.h" -#include "NimBLEDevice.h" -#include "NimBLELog.h" - -#define NULL_HANDLE (0xffff) -#define NIMBLE_SUB_NOTIFY 0x0001 -#define NIMBLE_SUB_INDICATE 0x0002 +# include "NimBLE2904.h" +# include "NimBLEDevice.h" +# include "NimBLELog.h" static NimBLECharacteristicCallbacks defaultCallback; -static const char* LOG_TAG = "NimBLECharacteristic"; - +static const char* LOG_TAG = "NimBLECharacteristic"; /** * @brief Construct a characteristic * @param [in] uuid - UUID (const char*) for the characteristic. * @param [in] properties - Properties for the characteristic. - * @param [in] max_len - The maximum length in bytes that the characteristic value can hold. (Default: 512 bytes for esp32, 20 for all others). + * @param [in] maxLen - The maximum length in bytes that the characteristic value can hold. (Default: 512 bytes for esp32, 20 for all others). * @param [in] pService - pointer to the service instance this characteristic belongs to. */ -NimBLECharacteristic::NimBLECharacteristic(const char* uuid, uint16_t properties, - uint16_t max_len, NimBLEService* pService) -: NimBLECharacteristic(NimBLEUUID(uuid), properties, max_len, pService) { -} +NimBLECharacteristic::NimBLECharacteristic(const char* uuid, uint16_t properties, uint16_t maxLen, NimBLEService* pService) + : NimBLECharacteristic(NimBLEUUID(uuid), properties, maxLen, pService) {} /** * @brief Construct a characteristic * @param [in] uuid - UUID for the characteristic. * @param [in] properties - Properties for the characteristic. - * @param [in] max_len - The maximum length in bytes that the characteristic value can hold. (Default: 512 bytes for esp32, 20 for all others). + * @param [in] maxLen - The maximum length in bytes that the characteristic value can hold. (Default: 512 bytes for esp32, 20 for all others). * @param [in] pService - pointer to the service instance this characteristic belongs to. */ -NimBLECharacteristic::NimBLECharacteristic(const NimBLEUUID &uuid, uint16_t properties, - uint16_t max_len, NimBLEService* pService) -: m_value(std::min(CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH , (int)max_len), max_len) { - m_uuid = uuid; - m_handle = NULL_HANDLE; - m_properties = properties; - m_pCallbacks = &defaultCallback; - m_pService = pService; - m_removed = 0; +NimBLECharacteristic::NimBLECharacteristic(const NimBLEUUID& uuid, uint16_t properties, uint16_t maxLen, NimBLEService* pService) + : NimBLELocalValueAttribute{uuid, 0, maxLen}, m_pCallbacks{&defaultCallback}, m_pService{pService} { + setProperties(properties); } // NimBLECharacteristic /** * @brief Destructor. */ NimBLECharacteristic::~NimBLECharacteristic() { - for(auto &it : m_dscVec) { - delete it; + for (const auto& dsc : m_vDescriptors) { + delete dsc; } } // ~NimBLECharacteristic - /** * @brief Create a new BLE Descriptor associated with this characteristic. * @param [in] uuid - The UUID of the descriptor. * @param [in] properties - The properties of the descriptor. - * @param [in] max_len - The max length in bytes of the descriptor value. + * @param [in] maxLen - The max length in bytes of the descriptor value. * @return The new BLE descriptor. */ -NimBLEDescriptor* NimBLECharacteristic::createDescriptor(const char* uuid, uint32_t properties, uint16_t max_len) { - return createDescriptor(NimBLEUUID(uuid), properties, max_len); +NimBLEDescriptor* NimBLECharacteristic::createDescriptor(const char* uuid, uint32_t properties, uint16_t maxLen) { + return createDescriptor(NimBLEUUID(uuid), properties, maxLen); } - /** * @brief Create a new BLE Descriptor associated with this characteristic. * @param [in] uuid - The UUID of the descriptor. * @param [in] properties - The properties of the descriptor. - * @param [in] max_len - The max length in bytes of the descriptor value. + * @param [in] maxLen - The max length in bytes of the descriptor value. * @return The new BLE descriptor. */ -NimBLEDescriptor* NimBLECharacteristic::createDescriptor(const NimBLEUUID &uuid, uint32_t properties, uint16_t max_len) { +NimBLEDescriptor* NimBLECharacteristic::createDescriptor(const NimBLEUUID& uuid, uint32_t properties, uint16_t maxLen) { NimBLEDescriptor* pDescriptor = nullptr; - if (uuid == NimBLEUUID(uint16_t(0x2904))) { - pDescriptor = new NimBLE2904(this); + if (uuid == NimBLEUUID(static_cast(0x2904))) { + NIMBLE_LOGW(LOG_TAG, "0x2904 descriptor should be created with create2904()"); + pDescriptor = create2904(); } else { - pDescriptor = new NimBLEDescriptor(uuid, properties, max_len, this); + pDescriptor = new NimBLEDescriptor(uuid, properties, maxLen, this); } addDescriptor(pDescriptor); return pDescriptor; } // createDescriptor +/** + * @brief Create a Characteristic Presentation Format Descriptor for this characteristic. + * @return A pointer to a NimBLE2904 descriptor. + */ +NimBLE2904* NimBLECharacteristic::create2904() { + NimBLE2904* pDescriptor = new NimBLE2904(this); + addDescriptor(pDescriptor); + return pDescriptor; +} // create2904 /** * @brief Add a descriptor to the characteristic. * @param [in] pDescriptor A pointer to the descriptor to add. */ -void NimBLECharacteristic::addDescriptor(NimBLEDescriptor *pDescriptor) { +void NimBLECharacteristic::addDescriptor(NimBLEDescriptor* pDescriptor) { bool foundRemoved = false; - - if(pDescriptor->m_removed > 0) { - for(auto& it : m_dscVec) { - if(it == pDescriptor) { + if (pDescriptor->getRemoved() > 0) { + for (const auto& dsc : m_vDescriptors) { + if (dsc == pDescriptor) { foundRemoved = true; - pDescriptor->m_removed = 0; + pDescriptor->setRemoved(0); } } } - if(!foundRemoved) { - m_dscVec.push_back(pDescriptor); + // Check if the descriptor is already in the vector and if so, return. + for (const auto& dsc : m_vDescriptors) { + if (dsc == pDescriptor) { + pDescriptor->setCharacteristic(this); // Update the characteristic pointer in the descriptor. + return; + } + } + + if (!foundRemoved) { + m_vDescriptors.push_back(pDescriptor); } pDescriptor->setCharacteristic(this); NimBLEDevice::getServer()->serviceChanged(); } - /** * @brief Remove a descriptor from the characteristic. * @param[in] pDescriptor A pointer to the descriptor instance to remove from the characteristic. * @param[in] deleteDsc If true it will delete the descriptor instance and free it's resources. */ -void NimBLECharacteristic::removeDescriptor(NimBLEDescriptor *pDescriptor, bool deleteDsc) { +void NimBLECharacteristic::removeDescriptor(NimBLEDescriptor* pDescriptor, bool deleteDsc) { // Check if the descriptor was already removed and if so, check if this // is being called to delete the object and do so if requested. // Otherwise, ignore the call and return. - if(pDescriptor->m_removed > 0) { - if(deleteDsc) { - for(auto it = m_dscVec.begin(); it != m_dscVec.end(); ++it) { + if (pDescriptor->getRemoved() > 0) { + if (deleteDsc) { + for (auto it = m_vDescriptors.begin(); it != m_vDescriptors.end(); ++it) { if ((*it) == pDescriptor) { - delete *it; - m_dscVec.erase(it); + delete (*it); + m_vDescriptors.erase(it); break; } } @@ -146,30 +151,28 @@ void NimBLECharacteristic::removeDescriptor(NimBLEDescriptor *pDescriptor, bool return; } - pDescriptor->m_removed = deleteDsc ? NIMBLE_ATT_REMOVE_DELETE : NIMBLE_ATT_REMOVE_HIDE; + pDescriptor->setRemoved(deleteDsc ? NIMBLE_ATT_REMOVE_DELETE : NIMBLE_ATT_REMOVE_HIDE); NimBLEDevice::getServer()->serviceChanged(); } // removeDescriptor - /** * @brief Return the BLE Descriptor for the given UUID. * @param [in] uuid The UUID of the descriptor. * @return A pointer to the descriptor object or nullptr if not found. */ -NimBLEDescriptor* NimBLECharacteristic::getDescriptorByUUID(const char* uuid) { +NimBLEDescriptor* NimBLECharacteristic::getDescriptorByUUID(const char* uuid) const { return getDescriptorByUUID(NimBLEUUID(uuid)); } // getDescriptorByUUID - /** * @brief Return the BLE Descriptor for the given UUID. * @param [in] uuid The UUID of the descriptor. * @return A pointer to the descriptor object or nullptr if not found. */ -NimBLEDescriptor* NimBLECharacteristic::getDescriptorByUUID(const NimBLEUUID &uuid) { - for (auto &it : m_dscVec) { - if (it->getUUID() == uuid) { - return it; +NimBLEDescriptor* NimBLECharacteristic::getDescriptorByUUID(const NimBLEUUID& uuid) const { + for (const auto& dsc : m_vDescriptors) { + if (dsc->getUUID() == uuid) { + return dsc; } } return nullptr; @@ -180,351 +183,152 @@ NimBLEDescriptor* NimBLECharacteristic::getDescriptorByUUID(const NimBLEUUID &uu * @param [in] handle The handle of the descriptor. * @return A pointer to the descriptor object or nullptr if not found. */ -NimBLEDescriptor *NimBLECharacteristic::getDescriptorByHandle(uint16_t handle) { - for (auto &it : m_dscVec) { - if (it->getHandle() == handle) { - return it; +NimBLEDescriptor* NimBLECharacteristic::getDescriptorByHandle(uint16_t handle) const { + for (const auto& dsc : m_vDescriptors) { + if (dsc->getHandle() == handle) { + return dsc; } } return nullptr; -} - - -/** - * @brief Get the handle of the characteristic. - * @return The handle of the characteristic. - */ -uint16_t NimBLECharacteristic::getHandle() { - return m_handle; -} // getHandle - +} // getDescriptorByHandle /** * @brief Get the properties of the characteristic. * @return The properties of the characteristic. */ -uint16_t NimBLECharacteristic::getProperties() { +uint16_t NimBLECharacteristic::getProperties() const { return m_properties; } // getProperties - /** - * @brief Get the service associated with this characteristic. + * @brief Get the service that owns this characteristic. */ -NimBLEService* NimBLECharacteristic::getService() { +NimBLEService* NimBLECharacteristic::getService() const { return m_pService; } // getService - -void NimBLECharacteristic::setService(NimBLEService *pService) { +void NimBLECharacteristic::setService(NimBLEService* pService) { m_pService = pService; -} - - -/** - * @brief Get the UUID of the characteristic. - * @return The UUID of the characteristic. - */ -NimBLEUUID NimBLECharacteristic::getUUID() { - return m_uuid; -} // getUUID - - -/** - * @brief Retrieve the current value of the characteristic. - * @return The NimBLEAttValue containing the current characteristic value. - */ -NimBLEAttValue NimBLECharacteristic::getValue(time_t *timestamp) { - if(timestamp != nullptr) { - m_value.getValue(timestamp); - } - - return m_value; -} // getValue - - -/** - * @brief Retrieve the the current data length of the characteristic. - * @return The length of the current characteristic data. - */ -size_t NimBLECharacteristic::getDataLength() { - return m_value.size(); -} - - -/** - * @brief STATIC callback to handle events from the NimBLE stack. - */ -int NimBLECharacteristic::handleGapEvent(uint16_t conn_handle, uint16_t attr_handle, - struct ble_gatt_access_ctxt *ctxt, - void *arg) -{ - if (conn_handle > BLE_HCI_LE_CONN_HANDLE_MAX) - { - NIMBLE_LOGW(LOG_TAG, "Conn_handle (%d) is above the maximum value (%d)", conn_handle, BLE_HCI_LE_CONN_HANDLE_MAX); - return BLE_ATT_ERR_INVALID_HANDLE; - } - - const ble_uuid_t *uuid; - int rc; - NimBLEConnInfo peerInfo; - NimBLECharacteristic* pCharacteristic = (NimBLECharacteristic*)arg; - - NIMBLE_LOGD(LOG_TAG, "Characteristic %s %s event", pCharacteristic->getUUID().toString().c_str(), - ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR ? "Read" : "Write"); - - uuid = ctxt->chr->uuid; - if(ble_uuid_cmp(uuid, &pCharacteristic->getUUID().getNative()->u) == 0){ - switch(ctxt->op) { - case BLE_GATT_ACCESS_OP_READ_CHR: { - ble_gap_conn_find(conn_handle, &peerInfo.m_desc); - - // If the packet header is only 8 bytes this is a follow up of a long read - // so we don't want to call the onRead() callback again. - if(ctxt->om->om_pkthdr_len > 8 || - conn_handle == BLE_HS_CONN_HANDLE_NONE || - pCharacteristic->m_value.size() <= (ble_att_mtu(peerInfo.m_desc.conn_handle) - 3)) { - pCharacteristic->m_pCallbacks->onRead(pCharacteristic, peerInfo); - } - - ble_npl_hw_enter_critical(); - rc = os_mbuf_append(ctxt->om, pCharacteristic->m_value.data(), pCharacteristic->m_value.size()); - ble_npl_hw_exit_critical(0); - return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; - } - - case BLE_GATT_ACCESS_OP_WRITE_CHR: { - uint16_t att_max_len = pCharacteristic->m_value.max_size(); - - if (ctxt->om->om_len > att_max_len) { - return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; - } - - uint8_t buf[att_max_len]; - size_t len = ctxt->om->om_len; - memcpy(buf, ctxt->om->om_data,len); - - os_mbuf *next; - next = SLIST_NEXT(ctxt->om, om_next); - while(next != NULL){ - if((len + next->om_len) > att_max_len) { - return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; - } - memcpy(&buf[len], next->om_data, next->om_len); - len += next->om_len; - next = SLIST_NEXT(next, om_next); - } - - ble_gap_conn_find(conn_handle, &peerInfo.m_desc); - pCharacteristic->setValue(buf, len); - pCharacteristic->m_pCallbacks->onWrite(pCharacteristic, peerInfo); - return 0; - } - default: - break; - } - } - - return BLE_ATT_ERR_UNLIKELY; -} - - -/** - * @brief Get the number of clients subscribed to the characteristic. - * @returns Number of clients subscribed to notifications / indications. - */ -size_t NimBLECharacteristic::getSubscribedCount() { - return m_subscribedVec.size(); -} - - -/** - * @brief Set the subscribe status for this characteristic.\n - * This will maintain a vector of subscribed clients and their indicate/notify status. - */ -void NimBLECharacteristic::setSubscribe(struct ble_gap_event *event) { - NimBLEConnInfo peerInfo; - if(ble_gap_conn_find(event->subscribe.conn_handle, &peerInfo.m_desc) != 0) { - return; - } - - uint16_t subVal = 0; - if(event->subscribe.cur_notify > 0 && (m_properties & NIMBLE_PROPERTY::NOTIFY)) { - subVal |= NIMBLE_SUB_NOTIFY; - } - if(event->subscribe.cur_indicate && (m_properties & NIMBLE_PROPERTY::INDICATE)) { - subVal |= NIMBLE_SUB_INDICATE; - } - - NIMBLE_LOGI(LOG_TAG, "New subscribe value for conn: %d val: %d", - event->subscribe.conn_handle, subVal); - - if(!event->subscribe.cur_indicate && event->subscribe.prev_indicate) { - NimBLEDevice::getServer()->clearIndicateWait(event->subscribe.conn_handle); - } - - - auto it = m_subscribedVec.begin(); - for(;it != m_subscribedVec.end(); ++it) { - if((*it).first == event->subscribe.conn_handle) { - break; - } - } - - if(subVal > 0) { - if(it == m_subscribedVec.end()) { - m_subscribedVec.push_back({event->subscribe.conn_handle, subVal}); - } else { - (*it).second = subVal; - } - } else if(it != m_subscribedVec.end()) { - m_subscribedVec.erase(it); - } - - m_pCallbacks->onSubscribe(this, peerInfo, subVal); -} - +} // setService /** * @brief Send an indication. + * @param[in] connHandle Connection handle to send an individual indication, or BLE_HS_CONN_HANDLE_NONE to send + * the indication to all subscribed clients. + * @return True if the indication was sent successfully, false otherwise. */ -void NimBLECharacteristic::indicate() { - notify(false); +bool NimBLECharacteristic::indicate(uint16_t connHandle) const { + return sendValue(nullptr, 0, false, connHandle); } // indicate - /** * @brief Send an indication. * @param[in] value A pointer to the data to send. * @param[in] length The length of the data to send. + * @param[in] connHandle Connection handle to send an individual indication, or BLE_HS_CONN_HANDLE_NONE to send + * the indication to all subscribed clients. + * @return True if the indication was sent successfully, false otherwise. */ -void NimBLECharacteristic::indicate(const uint8_t* value, size_t length) { - notify(value, length, false); +bool NimBLECharacteristic::indicate(const uint8_t* value, size_t length, uint16_t connHandle) const { + return sendValue(value, length, false, connHandle); } // indicate - /** - * @brief Send an indication. - * @param[in] value A std::vector containing the value to send as the notification value. + * @brief Send a notification. + * @param[in] connHandle Connection handle to send an individual notification, or BLE_HS_CONN_HANDLE_NONE to send + * the notification to all subscribed clients. + * @return True if the notification was sent successfully, false otherwise. */ -void NimBLECharacteristic::indicate(const std::vector& value) { - notify(value.data(), value.size(), false); -} // indicate - - -/** - * @brief Send a notification or indication. - * @param[in] is_notification if true sends a notification, false sends an indication. - * @param[in] conn_handle Connection handle to send individual notification, or BLE_HCI_LE_CONN_HANDLE_MAX + 1 to send notification to all subscribed clients. - */ -void NimBLECharacteristic::notify(bool is_notification, uint16_t conn_handle) { - notify(m_value.data(), m_value.length(), is_notification, conn_handle); +bool NimBLECharacteristic::notify(uint16_t connHandle) const { + return sendValue(nullptr, 0, true, connHandle); } // notify - /** - * @brief Send a notification or indication. - * @param[in] value A std::vector containing the value to send as the notification value. - * @param[in] is_notification if true sends a notification, false sends an indication. - * @param[in] conn_handle Connection handle to send individual notification, or BLE_HCI_LE_CONN_HANDLE_MAX + 1 to send notification to all subscribed clients. - */ -void NimBLECharacteristic::notify(const std::vector& value, bool is_notification, uint16_t conn_handle) { - notify(value.data(), value.size(), is_notification, conn_handle); -} // notify - - -/** - * @brief Send a notification or indication. + * @brief Send a notification. * @param[in] value A pointer to the data to send. * @param[in] length The length of the data to send. - * @param[in] is_notification if true sends a notification, false sends an indication. - * @param[in] conn_handle Connection handle to send individual notification, or BLE_HCI_LE_CONN_HANDLE_MAX + 1 to send notification to all subscribed clients. + * @param[in] connHandle Connection handle to send an individual notification, or BLE_HS_CONN_HANDLE_NONE to send + * the notification to all subscribed clients. + * @return True if the notification was sent successfully, false otherwise. */ -void NimBLECharacteristic::notify(const uint8_t* value, size_t length, bool is_notification, uint16_t conn_handle) { - NIMBLE_LOGD(LOG_TAG, ">> notify: length: %d", length); +bool NimBLECharacteristic::notify(const uint8_t* value, size_t length, uint16_t connHandle) const { + return sendValue(value, length, true, connHandle); +} // indicate - if(!(m_properties & NIMBLE_PROPERTY::NOTIFY) && - !(m_properties & NIMBLE_PROPERTY::INDICATE)) - { - NIMBLE_LOGE(LOG_TAG, - "<< notify-Error; Notify/indicate not enabled for characteristic: %s", - std::string(getUUID()).c_str()); - } - - if (m_subscribedVec.size() == 0) { - NIMBLE_LOGD(LOG_TAG, "<< notify: No clients subscribed."); - return; - } - - m_pCallbacks->onNotify(this); - - bool reqSec = (m_properties & BLE_GATT_CHR_F_READ_AUTHEN) || - (m_properties & BLE_GATT_CHR_F_READ_AUTHOR) || - (m_properties & BLE_GATT_CHR_F_READ_ENC); +/** + * @brief Sends a notification or indication. + * @param[in] value A pointer to the data to send. + * @param[in] length The length of the data to send. + * @param[in] isNotification if true sends a notification, false sends an indication. + * @param[in] connHandle Connection handle to send to a specific peer. + * @return True if the value was sent successfully, false otherwise. + */ +bool NimBLECharacteristic::sendValue(const uint8_t* value, size_t length, bool isNotification, uint16_t connHandle) const { int rc = 0; - for (auto &it : m_subscribedVec) { - // check if need a specific client - if ((conn_handle <= BLE_HCI_LE_CONN_HANDLE_MAX) && (it.first != conn_handle)) { - continue; - } + if (value != nullptr && length > 0) { // custom notification value + os_mbuf* om = nullptr; - uint16_t _mtu = getService()->getServer()->getPeerMTU(it.first) - 3; - - // check if connected and subscribed - if(_mtu == 0 || it.second == 0) { - continue; - } - - // check if security requirements are satisfied - if(reqSec) { - struct ble_gap_conn_desc desc; - rc = ble_gap_conn_find(it.first, &desc); - if(rc != 0 || !desc.sec_state.encrypted) { - continue; - } - } - - if (length > _mtu) { - NIMBLE_LOGW(LOG_TAG, "- Truncating to %d bytes (maximum notify size)", _mtu); - } - - if(is_notification && (!(it.second & NIMBLE_SUB_NOTIFY))) { - NIMBLE_LOGW(LOG_TAG, - "Sending notification to client subscribed to indications, sending indication instead"); - is_notification = false; - } - - if(!is_notification && (!(it.second & NIMBLE_SUB_INDICATE))) { - NIMBLE_LOGW(LOG_TAG, - "Sending indication to client subscribed to notification, sending notification instead"); - is_notification = true; - } - - // don't create the m_buf until we are sure to send the data or else - // we could be allocating a buffer that doesn't get released. - // We also must create it in each loop iteration because it is consumed with each host call. - os_mbuf *om = ble_hs_mbuf_from_flat(value, length); - - if(!is_notification && (m_properties & NIMBLE_PROPERTY::INDICATE)) { - if(!NimBLEDevice::getServer()->setIndicateWait(it.first)) { - NIMBLE_LOGE(LOG_TAG, "prior Indication in progress"); - os_mbuf_free_chain(om); - return; + if (connHandle != BLE_HS_CONN_HANDLE_NONE) { // only sending to specific peer + om = ble_hs_mbuf_from_flat(value, length); + if (!om) { + rc = BLE_HS_ENOMEM; + goto done; } - rc = ble_gattc_indicate_custom(it.first, m_handle, om); - if(rc != 0){ - NimBLEDevice::getServer()->clearIndicateWait(it.first); + // Null buffer will read the value from the characteristic + if (isNotification) { + rc = ble_gattc_notify_custom(connHandle, m_handle, om); + } else { + rc = ble_gattc_indicate_custom(connHandle, m_handle, om); } + + goto done; + } + + // Notify all connected peers unless a specific handle is provided + for (const auto& ch : NimBLEDevice::getServer()->getPeerDevices()) { + // Must re-create the data buffer on each iteration because it is freed by the calls bellow. + om = ble_hs_mbuf_from_flat(value, length); + if (!om) { + rc = BLE_HS_ENOMEM; + goto done; + } + + if (isNotification) { + rc = ble_gattc_notify_custom(ch, m_handle, om); + } else { + rc = ble_gattc_indicate_custom(ch, m_handle, om); + } + } + } else if (connHandle != BLE_HS_CONN_HANDLE_NONE) { + // Null buffer will read the value from the characteristic + if (isNotification) { + rc = ble_gattc_notify_custom(connHandle, m_handle, nullptr); } else { - ble_gattc_notify_custom(it.first, m_handle, om); + rc = ble_gattc_indicate_custom(connHandle, m_handle, nullptr); } + } else { // Notify or indicate to all connected peers the characteristic value + ble_gatts_chr_updated(m_handle); } - NIMBLE_LOGD(LOG_TAG, "<< notify"); -} // Notify +done: + if (rc != 0) { + NIMBLE_LOGE(LOG_TAG, "failed to send value, rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc)); + return false; + } + return true; +} // sendValue + +void NimBLECharacteristic::readEvent(NimBLEConnInfo& connInfo) { + m_pCallbacks->onRead(this, connInfo); +} // readEvent + +void NimBLECharacteristic::writeEvent(const uint8_t* val, uint16_t len, NimBLEConnInfo& connInfo) { + setValue(val, len); + m_pCallbacks->onWrite(this, connInfo); +} // writeEvent /** * @brief Set the callback handlers for this characteristic. @@ -532,60 +336,31 @@ void NimBLECharacteristic::notify(const uint8_t* value, size_t length, bool is_n * used to define any callbacks for the characteristic. */ void NimBLECharacteristic::setCallbacks(NimBLECharacteristicCallbacks* pCallbacks) { - if (pCallbacks != nullptr){ + if (pCallbacks != nullptr) { m_pCallbacks = pCallbacks; } else { m_pCallbacks = &defaultCallback; } } // setCallbacks - /** * @brief Get the callback handlers for this characteristic. */ -NimBLECharacteristicCallbacks* NimBLECharacteristic::getCallbacks() { +NimBLECharacteristicCallbacks* NimBLECharacteristic::getCallbacks() const { return m_pCallbacks; -} //getCallbacks - - -/** - * @brief Set the value of the characteristic from a data buffer . - * @param [in] data The data buffer to set for the characteristic. - * @param [in] length The number of bytes in the data buffer. - */ -void NimBLECharacteristic::setValue(const uint8_t* data, size_t length) { -#if CONFIG_NIMBLE_CPP_LOG_LEVEL >= 4 - char* pHex = NimBLEUtils::buildHexData(nullptr, data, length); - NIMBLE_LOGD(LOG_TAG, ">> setValue: length=%d, data=%s, characteristic UUID=%s", - length, pHex, getUUID().toString().c_str()); - free(pHex); -#endif - - m_value.setValue(data, length); - NIMBLE_LOGD(LOG_TAG, "<< setValue"); -} // setValue - - -/** - * @brief Set the value of the characteristic from a `std::vector`.\n - * @param [in] vec The std::vector reference to set the characteristic value from. - */ -void NimBLECharacteristic::setValue(const std::vector& vec) { - return setValue((uint8_t*)&vec[0], vec.size()); -}// setValue - +} // getCallbacks /** * @brief Return a string representation of the characteristic. * @return A string representation of the characteristic. */ -std::string NimBLECharacteristic::toString() { +std::string NimBLECharacteristic::toString() const { std::string res = "UUID: " + m_uuid.toString() + ", handle : 0x"; - char hex[5]; - snprintf(hex, sizeof(hex), "%04x", m_handle); + char hex[5]; + snprintf(hex, sizeof(hex), "%04x", getHandle()); res += hex; res += " "; - if (m_properties & BLE_GATT_CHR_PROP_READ ) res += "Read "; + if (m_properties & BLE_GATT_CHR_PROP_READ) res += "Read "; if (m_properties & BLE_GATT_CHR_PROP_WRITE) res += "Write "; if (m_properties & BLE_GATT_CHR_PROP_WRITE_NO_RSP) res += "WriteNoResponse "; if (m_properties & BLE_GATT_CHR_PROP_BROADCAST) res += "Broadcast "; @@ -594,7 +369,6 @@ std::string NimBLECharacteristic::toString() { return res; } // toString - /** * @brief Callback function to support a read request. * @param [in] pCharacteristic The characteristic that is the source of the event. @@ -604,7 +378,6 @@ void NimBLECharacteristicCallbacks::onRead(NimBLECharacteristic* pCharacteristic NIMBLE_LOGD("NimBLECharacteristicCallbacks", "onRead: default"); } // onRead - /** * @brief Callback function to support a write request. * @param [in] pCharacteristic The characteristic that is the source of the event. @@ -614,16 +387,6 @@ void NimBLECharacteristicCallbacks::onWrite(NimBLECharacteristic* pCharacteristi NIMBLE_LOGD("NimBLECharacteristicCallbacks", "onWrite: default"); } // onWrite - -/** - * @brief Callback function to support a Notify request. - * @param [in] pCharacteristic The characteristic that is the source of the event. - */ -void NimBLECharacteristicCallbacks::onNotify(NimBLECharacteristic* pCharacteristic) { - NIMBLE_LOGD("NimBLECharacteristicCallbacks", "onNotify: default"); -} // onNotify - - /** * @brief Callback function to support a Notify/Indicate Status report. * @param [in] pCharacteristic The characteristic that is the source of the event. @@ -635,7 +398,6 @@ void NimBLECharacteristicCallbacks::onStatus(NimBLECharacteristic* pCharacterist NIMBLE_LOGD("NimBLECharacteristicCallbacks", "onStatus: default"); } // onStatus - /** * @brief Callback function called when a client changes subscription status. * @param [in] pCharacteristic The characteristic that is the source of the event. @@ -647,10 +409,9 @@ void NimBLECharacteristicCallbacks::onStatus(NimBLECharacteristic* pCharacterist * * 3 = Notifications and Indications */ void NimBLECharacteristicCallbacks::onSubscribe(NimBLECharacteristic* pCharacteristic, - NimBLEConnInfo& connInfo, - uint16_t subValue) -{ + NimBLEConnInfo& connInfo, + uint16_t subValue) { NIMBLE_LOGD("NimBLECharacteristicCallbacks", "onSubscribe: default"); } -#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL */ +#endif // CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL diff --git a/lib/libesp32_div/esp-nimble-cpp/src/NimBLECharacteristic.h b/lib/libesp32_div/esp-nimble-cpp/src/NimBLECharacteristic.h index 494242c70..02ae89a71 100644 --- a/lib/libesp32_div/esp-nimble-cpp/src/NimBLECharacteristic.h +++ b/lib/libesp32_div/esp-nimble-cpp/src/NimBLECharacteristic.h @@ -1,197 +1,243 @@ /* - * NimBLECharacteristic.h + * Copyright 2020-2025 Ryan Powell and + * esp-nimble-cpp, NimBLE-Arduino contributors. * - * Created: on March 3, 2020 - * Author H2zero + * 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 * - * Originally: - * BLECharacteristic.h + * http://www.apache.org/licenses/LICENSE-2.0 * - * Created on: Jun 22, 2017 - * Author: kolban + * 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 MAIN_NIMBLECHARACTERISTIC_H_ -#define MAIN_NIMBLECHARACTERISTIC_H_ +#ifndef NIMBLE_CPP_CHARACTERISTIC_H_ +#define NIMBLE_CPP_CHARACTERISTIC_H_ + #include "nimconfig.h" -#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) +#if CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL -#if defined(CONFIG_NIMBLE_CPP_IDF) -#include "host/ble_hs.h" -#else -#include "nimble/nimble/host/include/host/ble_hs.h" -#endif - -/**** FIX COMPILATION ****/ -#undef min -#undef max -/**************************/ - -typedef enum { - READ = BLE_GATT_CHR_F_READ, - READ_ENC = BLE_GATT_CHR_F_READ_ENC, - READ_AUTHEN = BLE_GATT_CHR_F_READ_AUTHEN, - READ_AUTHOR = BLE_GATT_CHR_F_READ_AUTHOR, - WRITE = BLE_GATT_CHR_F_WRITE, - WRITE_NR = BLE_GATT_CHR_F_WRITE_NO_RSP, - WRITE_ENC = BLE_GATT_CHR_F_WRITE_ENC, - WRITE_AUTHEN = BLE_GATT_CHR_F_WRITE_AUTHEN, - WRITE_AUTHOR = BLE_GATT_CHR_F_WRITE_AUTHOR, - BROADCAST = BLE_GATT_CHR_F_BROADCAST, - NOTIFY = BLE_GATT_CHR_F_NOTIFY, - INDICATE = BLE_GATT_CHR_F_INDICATE -} NIMBLE_PROPERTY; - -#include "NimBLEService.h" -#include "NimBLEDescriptor.h" -#include "NimBLEAttValue.h" -#include "NimBLEConnInfo.h" - -#include -#include - -class NimBLEService; -class NimBLEDescriptor; class NimBLECharacteristicCallbacks; +class NimBLEService; +class NimBLECharacteristic; +class NimBLEDescriptor; +class NimBLE2904; +# include "NimBLELocalValueAttribute.h" + +# include +# include /** - * @brief The model of a %BLE Characteristic. + * @brief The model of a BLE Characteristic. * - * A BLE Characteristic is an identified value container that manages a value. It is exposed by a BLE server and - * can be read and written to by a %BLE client. + * A BLE Characteristic is an identified value container that manages a value. It is exposed by a BLE service and + * can be read and written to by a BLE client. */ -class NimBLECharacteristic { -public: - NimBLECharacteristic(const char* uuid, - uint16_t properties = - NIMBLE_PROPERTY::READ | - NIMBLE_PROPERTY::WRITE, - uint16_t max_len = BLE_ATT_ATTR_MAX_LEN, - NimBLEService* pService = nullptr); - NimBLECharacteristic(const NimBLEUUID &uuid, - uint16_t properties = - NIMBLE_PROPERTY::READ | - NIMBLE_PROPERTY::WRITE, - uint16_t max_len = BLE_ATT_ATTR_MAX_LEN, - NimBLEService* pService = nullptr); +class NimBLECharacteristic : public NimBLELocalValueAttribute { + public: + NimBLECharacteristic(const char* uuid, + uint16_t properties = NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE, + uint16_t maxLen = BLE_ATT_ATTR_MAX_LEN, + NimBLEService* pService = nullptr); + NimBLECharacteristic(const NimBLEUUID& uuid, + uint16_t properties = NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE, + uint16_t maxLen = BLE_ATT_ATTR_MAX_LEN, + NimBLEService* pService = nullptr); ~NimBLECharacteristic(); - uint16_t getHandle(); - NimBLEUUID getUUID(); - std::string toString(); - void indicate(); - void indicate(const uint8_t* value, size_t length); - void indicate(const std::vector& value); - void notify(bool is_notification = true, uint16_t conn_handle = BLE_HCI_LE_CONN_HANDLE_MAX + 1); - void notify(const uint8_t* value, size_t length, bool is_notification = true, uint16_t conn_handle = BLE_HCI_LE_CONN_HANDLE_MAX + 1); - void notify(const std::vector& value, bool is_notification = true, uint16_t conn_handle = BLE_HCI_LE_CONN_HANDLE_MAX + 1); - size_t getSubscribedCount(); - void addDescriptor(NimBLEDescriptor *pDescriptor); - NimBLEDescriptor* getDescriptorByUUID(const char* uuid); - NimBLEDescriptor* getDescriptorByUUID(const NimBLEUUID &uuid); - NimBLEDescriptor* getDescriptorByHandle(uint16_t handle); - void removeDescriptor(NimBLEDescriptor *pDescriptor, bool deleteDsc = false); - NimBLEService* getService(); - uint16_t getProperties(); - NimBLEAttValue getValue(time_t *timestamp = nullptr); - size_t getDataLength(); - void setValue(const uint8_t* data, size_t size); - void setValue(const std::vector& vec); - void setCallbacks(NimBLECharacteristicCallbacks* pCallbacks); + std::string toString() const; + void addDescriptor(NimBLEDescriptor* pDescriptor); + void removeDescriptor(NimBLEDescriptor* pDescriptor, bool deleteDsc = false); + uint16_t getProperties() const; + void setCallbacks(NimBLECharacteristicCallbacks* pCallbacks); + bool indicate(uint16_t connHandle = BLE_HS_CONN_HANDLE_NONE) const; + bool indicate(const uint8_t* value, size_t length, uint16_t connHandle = BLE_HS_CONN_HANDLE_NONE) const; + bool notify(uint16_t connHandle = BLE_HS_CONN_HANDLE_NONE) const; + bool notify(const uint8_t* value, size_t length, uint16_t connHandle = BLE_HS_CONN_HANDLE_NONE) const; + NimBLEDescriptor* createDescriptor(const char* uuid, - uint32_t properties = - NIMBLE_PROPERTY::READ | - NIMBLE_PROPERTY::WRITE, - uint16_t max_len = BLE_ATT_ATTR_MAX_LEN);; - NimBLEDescriptor* createDescriptor(const NimBLEUUID &uuid, - uint32_t properties = - NIMBLE_PROPERTY::READ | - NIMBLE_PROPERTY::WRITE, - uint16_t max_len = BLE_ATT_ATTR_MAX_LEN); - - NimBLECharacteristicCallbacks* getCallbacks(); + uint32_t properties = NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE, + uint16_t maxLen = BLE_ATT_ATTR_MAX_LEN); + NimBLEDescriptor* createDescriptor(const NimBLEUUID& uuid, + uint32_t properties = NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE, + uint16_t maxLen = BLE_ATT_ATTR_MAX_LEN); + NimBLE2904* create2904(); + NimBLEDescriptor* getDescriptorByUUID(const char* uuid) const; + NimBLEDescriptor* getDescriptorByUUID(const NimBLEUUID& uuid) const; + NimBLEDescriptor* getDescriptorByHandle(uint16_t handle) const; + NimBLEService* getService() const; + NimBLECharacteristicCallbacks* getCallbacks() const; /*********************** Template Functions ************************/ +# if __cplusplus < 201703L /** - * @brief Template to set the characteristic value to val. - * @param [in] s The value to set. + * @brief Template to send a notification with a value from a struct or array. + * @param [in] v The value to send. + * @param [in] connHandle Optional, a connection handle to send the notification to. + * @details size must be evaluatable by `sizeof()`. */ - template - void setValue(const T &s) { m_value.setValue(s); } - - /** - * @brief Template to convert the characteristic data to . - * @tparam T The type to convert the data to. - * @param [in] timestamp (Optional) A pointer to a time_t struct to store the time the value was read. - * @param [in] skipSizeCheck (Optional) If true it will skip checking if the data size is less than sizeof(). - * @return The data converted to or NULL if skipSizeCheck is false and the data is less than sizeof(). - * @details Use: getValue(×tamp, skipSizeCheck); - */ - template - T getValue(time_t *timestamp = nullptr, bool skipSizeCheck = false) { - return m_value.getValue(timestamp, skipSizeCheck); + template +# ifdef _DOXYGEN_ + bool +# else + typename std::enable_if::value && !std::is_array::value && !Has_c_str_length::value && + !Has_data_size::value, + bool>::type +# endif + notify(const T& v, uint16_t connHandle = BLE_HS_CONN_HANDLE_NONE) const { + return notify(reinterpret_cast(&v), sizeof(T), connHandle); } /** - * @brief Template to send a notification from a class type that has a c_str() and length() method. + * @brief Template to send a notification with a value from a class that has a c_str() and length() method. + * @param [in] s The value to send. + * @param [in] connHandle Optional, a connection handle to send the notification to. + */ + template +# ifdef _DOXYGEN_ + bool +# else + typename std::enable_if::value && !Has_data_size::value, bool>::type +# endif + notify(const T& s, uint16_t connHandle = BLE_HS_CONN_HANDLE_NONE) const { + return notify(reinterpret_cast(s.c_str()), s.length(), connHandle); + } + + /** + * @brief Template to send a notification with a value from a class that has a data() and size() method. + * @param [in] v The value to send. + * @param [in] connHandle Optional, a connection handle to send the notification to. + */ + template +# ifdef _DOXYGEN_ + bool +# else + typename std::enable_if::value, bool>::type +# endif + notify(const T& v, uint16_t connHandle = BLE_HS_CONN_HANDLE_NONE) const { + return notify(reinterpret_cast(v.data()), v.size(), connHandle); + } + + /** + * @brief Template to send an indication with a value from a struct or array. + * @param [in] v The value to send. + * @param [in] connHandle Optional, a connection handle to send the notification to. + * @details size must be evaluatable by `sizeof()`. + */ + template +# ifdef _DOXYGEN_ + bool +# else + typename std::enable_if::value && !std::is_array::value && !Has_c_str_length::value && + !Has_data_size::value, + bool>::type +# endif + indicate(const T& v, uint16_t connHandle = BLE_HS_CONN_HANDLE_NONE) const { + return indicate(reinterpret_cast(&v), sizeof(T), connHandle); + } + + /** + * @brief Template to send a indication with a value from a class that has a c_str() and length() method. + * @param [in] s The value to send. + * @param [in] connHandle Optional, a connection handle to send the notification to. + */ + template +# ifdef _DOXYGEN_ + bool +# else + typename std::enable_if::value && !Has_data_size::value, bool>::type +# endif + indicate(const T& s, uint16_t connHandle = BLE_HS_CONN_HANDLE_NONE) const { + return indicate(reinterpret_cast(s.c_str()), s.length(), connHandle); + } + + /** + * @brief Template to send a indication with a value from a class that has a data() and size() method. + * @param [in] v The value to send. + * @param [in] connHandle Optional, a connection handle to send the notification to. + */ + template +# ifdef _DOXYGEN_ + bool +# else + typename std::enable_if::value, bool>::type +# endif + indicate(const T& v, uint16_t connHandle = BLE_HS_CONN_HANDLE_NONE) const { + return indicate(reinterpret_cast(v.data()), v.size(), connHandle); + } + +# else + + /** + * @brief Template to send a notification for classes which may have + * data()/size() or c_str()/length() methods. Falls back to sending + * the data by casting the first element of the array to a uint8_t + * pointer and getting the length of the array using sizeof. * @tparam T The a reference to a class containing the data to send. * @param[in] value The value to set. - * @param[in] is_notification if true sends a notification, false sends an indication. - * @details Only used if the has a `c_str()` method. + * @param[in] connHandle The connection handle to send the notification to. + * @note This function is only available if the type T is not a pointer. */ - template -#ifdef _DOXYGEN_ - void -#else - typename std::enable_if::value, void>::type -#endif - notify(const T& value, bool is_notification = true) { - notify((uint8_t*)value.c_str(), value.length(), is_notification); + template + typename std::enable_if::value && !std::is_array::value, bool>::type notify( + const T& value, uint16_t connHandle = BLE_HS_CONN_HANDLE_NONE) const { + if constexpr (Has_data_size::value) { + return notify(reinterpret_cast(value.data()), value.size(), connHandle); + } else if constexpr (Has_c_str_length::value) { + return notify(reinterpret_cast(value.c_str()), value.length(), connHandle); + } else { + return notify(reinterpret_cast(&value), sizeof(value), connHandle); + } } /** - * @brief Template to send an indication from a class type that has a c_str() and length() method. + * @brief Template to send an indication for classes which may have + * data()/size() or c_str()/length() methods. Falls back to sending + * the data by casting the first element of the array to a uint8_t + * pointer and getting the length of the array using sizeof. * @tparam T The a reference to a class containing the data to send. * @param[in] value The value to set. - * @details Only used if the has a `c_str()` method. + * @param[in] connHandle The connection handle to send the indication to. + * @note This function is only available if the type T is not a pointer. */ - template -#ifdef _DOXYGEN_ - void -#else - typename std::enable_if::value, void>::type -#endif - indicate(const T& value) { - indicate((uint8_t*)value.c_str(), value.length()); + template + typename std::enable_if::value && !std::is_array::value, bool>::type indicate( + const T& value, uint16_t connHandle = BLE_HS_CONN_HANDLE_NONE) const { + if constexpr (Has_data_size::value) { + return indicate(reinterpret_cast(value.data()), value.size(), connHandle); + } else if constexpr (Has_c_str_length::value) { + return indicate(reinterpret_cast(value.c_str()), value.length(), connHandle); + } else { + return indicate(reinterpret_cast(&value), sizeof(value), connHandle); + } } +# endif -private: + private: + friend class NimBLEServer; + friend class NimBLEService; - friend class NimBLEServer; - friend class NimBLEService; + void setService(NimBLEService* pService); + void readEvent(NimBLEConnInfo& connInfo) override; + void writeEvent(const uint8_t* val, uint16_t len, NimBLEConnInfo& connInfo) override; + bool sendValue(const uint8_t* value, + size_t length, + bool is_notification = true, + uint16_t connHandle = BLE_HS_CONN_HANDLE_NONE) const; - void setService(NimBLEService *pService); - void setSubscribe(struct ble_gap_event *event); - static int handleGapEvent(uint16_t conn_handle, uint16_t attr_handle, - struct ble_gatt_access_ctxt *ctxt, void *arg); - - NimBLEUUID m_uuid; - uint16_t m_handle; - uint16_t m_properties; - NimBLECharacteristicCallbacks* m_pCallbacks; - NimBLEService* m_pService; - NimBLEAttValue m_value; - std::vector m_dscVec; - uint8_t m_removed; - - std::vector> m_subscribedVec; + NimBLECharacteristicCallbacks* m_pCallbacks{nullptr}; + NimBLEService* m_pService{nullptr}; + std::vector m_vDescriptors{}; }; // NimBLECharacteristic - /** * @brief Callbacks that can be associated with a %BLE characteristic to inform of events. * @@ -200,14 +246,13 @@ private: * sub-classed instance of this class and will be notified when such an event happens. */ class NimBLECharacteristicCallbacks { -public: - virtual ~NimBLECharacteristicCallbacks(){} + public: + virtual ~NimBLECharacteristicCallbacks() {} virtual void onRead(NimBLECharacteristic* pCharacteristic, NimBLEConnInfo& connInfo); virtual void onWrite(NimBLECharacteristic* pCharacteristic, NimBLEConnInfo& connInfo); - virtual void onNotify(NimBLECharacteristic* pCharacteristic); virtual void onStatus(NimBLECharacteristic* pCharacteristic, int code); virtual void onSubscribe(NimBLECharacteristic* pCharacteristic, NimBLEConnInfo& connInfo, uint16_t subValue); }; -#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL */ -#endif /*MAIN_NIMBLECHARACTERISTIC_H_*/ +#endif // CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL +#endif // NIMBLE_CPP_CHARACTERISTIC_H_ diff --git a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEClient.cpp b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEClient.cpp index e49827f42..5415de307 100644 --- a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEClient.cpp +++ b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEClient.cpp @@ -1,34 +1,37 @@ /* - * NimBLEClient.cpp + * Copyright 2020-2025 Ryan Powell and + * esp-nimble-cpp, NimBLE-Arduino contributors. * - * Created: on Jan 26 2020 - * Author H2zero + * 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 * - * Originally: - * BLEClient.cpp + * http://www.apache.org/licenses/LICENSE-2.0 * - * Created on: Mar 22, 2017 - * Author: kolban + * 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 "nimconfig.h" -#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL) - #include "NimBLEClient.h" -#include "NimBLEDevice.h" -#include "NimBLELog.h" +#if CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL -#include -#include -#include +# include "NimBLERemoteService.h" +# include "NimBLERemoteCharacteristic.h" +# include "NimBLEDevice.h" +# include "NimBLELog.h" -#if defined(CONFIG_NIMBLE_CPP_IDF) -#include "nimble/nimble_port.h" -#else -#include "nimble/porting/nimble/include/nimble/nimble_port.h" -#endif +# if defined(CONFIG_NIMBLE_CPP_IDF) +# include "nimble/nimble_port.h" +# else +# include "nimble/porting/nimble/include/nimble/nimble_port.h" +# endif -static const char* LOG_TAG = "NimBLEClient"; +# include + +static const char* LOG_TAG = "NimBLEClient"; static NimBLEClientCallbacks defaultCallbacks; /* @@ -44,48 +47,40 @@ static NimBLEClientCallbacks defaultCallbacks; * * NimBLERemoteDescriptor - A model of a remote descriptor. * * Since there is a hierarchical relationship here, we will have the idea that from a NimBLERemoteService will own - * zero or more remote characteristics and a NimBLERemoteCharacteristic will own zero or more remote NimBLEDescriptors. + * zero or more remote characteristics and a NimBLERemoteCharacteristic will own zero or more NimBLERemoteDescriptors. * * We will assume that a NimBLERemoteService contains a vector of owned characteristics - * and that a NimBLECharacteristic contains a vector of owned descriptors. - * - * + * and that a NimBLERemoteCharacteristic contains a vector of owned descriptors. */ - /** * @brief Constructor, private - only callable by NimBLEDevice::createClient * to ensure proper handling of the list of client objects. */ -NimBLEClient::NimBLEClient(const NimBLEAddress &peerAddress) : m_peerAddress(peerAddress) { - m_pClientCallbacks = &defaultCallbacks; - m_conn_id = BLE_HS_CONN_HANDLE_NONE; - m_connectTimeout = 30000; - m_deleteCallbacks = false; - m_pTaskData = nullptr; - m_connEstablished = false; - m_lastErr = 0; -#if CONFIG_BT_NIMBLE_EXT_ADV - m_phyMask = BLE_GAP_LE_PHY_1M_MASK | - BLE_GAP_LE_PHY_2M_MASK | - BLE_GAP_LE_PHY_CODED_MASK; -#endif - - m_pConnParams.scan_itvl = 16; // Scan interval in 0.625ms units (NimBLE Default) - m_pConnParams.scan_window = 16; // Scan window in 0.625ms units (NimBLE Default) - m_pConnParams.itvl_min = BLE_GAP_INITIAL_CONN_ITVL_MIN; // min_int = 0x10*1.25ms = 20ms - m_pConnParams.itvl_max = BLE_GAP_INITIAL_CONN_ITVL_MAX; // max_int = 0x20*1.25ms = 40ms - m_pConnParams.latency = BLE_GAP_INITIAL_CONN_LATENCY; // number of packets allowed to skip (extends max interval) - m_pConnParams.supervision_timeout = BLE_GAP_INITIAL_SUPERVISION_TIMEOUT; // timeout = 400*10ms = 4000ms - m_pConnParams.min_ce_len = BLE_GAP_INITIAL_CONN_MIN_CE_LEN; // Minimum length of connection event in 0.625ms units - m_pConnParams.max_ce_len = BLE_GAP_INITIAL_CONN_MAX_CE_LEN; // Maximum length of connection event in 0.625ms units - - memset(&m_dcTimer, 0, sizeof(m_dcTimer)); - ble_npl_callout_init(&m_dcTimer, nimble_port_get_dflt_eventq(), - NimBLEClient::dcTimerCb, this); +NimBLEClient::NimBLEClient(const NimBLEAddress& peerAddress) + : m_peerAddress(peerAddress), + m_lastErr{0}, + m_connectTimeout{30000}, + m_pTaskData{nullptr}, + m_svcVec{}, + m_pClientCallbacks{&defaultCallbacks}, + m_connHandle{BLE_HS_CONN_HANDLE_NONE}, + m_terminateFailCount{0}, + m_asyncSecureAttempt{0}, + m_config{}, +# if CONFIG_BT_NIMBLE_EXT_ADV + m_phyMask{BLE_GAP_LE_PHY_1M_MASK | BLE_GAP_LE_PHY_2M_MASK | BLE_GAP_LE_PHY_CODED_MASK}, +# endif + m_connParams{16, + 16, + BLE_GAP_INITIAL_CONN_ITVL_MIN, + BLE_GAP_INITIAL_CONN_ITVL_MAX, + BLE_GAP_INITIAL_CONN_LATENCY, + BLE_GAP_INITIAL_SUPERVISION_TIMEOUT, + BLE_GAP_INITIAL_CONN_MIN_CE_LEN, + BLE_GAP_INITIAL_CONN_MAX_CE_LEN} { } // NimBLEClient - /** * @brief Destructor, private - only callable by NimBLEDevice::deleteClient * to ensure proper disconnect and removal from device list. @@ -95,330 +90,319 @@ NimBLEClient::~NimBLEClient() { // Before we are finished with the client, we must release resources. deleteServices(); - if(m_deleteCallbacks && m_pClientCallbacks != &defaultCallbacks) { + if (m_config.deleteCallbacks) { delete m_pClientCallbacks; } - - ble_npl_callout_deinit(&m_dcTimer); - } // ~NimBLEClient - -/** - * @brief If we have asked to disconnect and the event does not - * occur within the supervision timeout + added delay, this will - * be called to reset the host in the case of a stalled controller. - */ -void NimBLEClient::dcTimerCb(ble_npl_event *event) { - /* NimBLEClient *pClient = (NimBLEClient*)event->arg; - NIMBLE_LOGE(LOG_TAG, "Timed out disconnecting from %s - resetting host", - std::string(pClient->getPeerAddress()).c_str()); - */ - ble_hs_sched_reset(BLE_HS_ECONTROLLER); -} - - /** * @brief Delete all service objects created by this client and clear the vector. */ void NimBLEClient::deleteServices() { - NIMBLE_LOGD(LOG_TAG, ">> deleteServices"); // Delete all the services. - for(auto &it: m_servicesVector) { + for (auto& it : m_svcVec) { delete it; } - m_servicesVector.clear(); - NIMBLE_LOGD(LOG_TAG, "<< deleteServices"); + std::vector().swap(m_svcVec); } // deleteServices - /** - * @brief Delete service by UUID - * @param [in] uuid The UUID of the service to be deleted from the local database. + * @brief Delete a service by UUID from the local database to free resources. + * @param [in] uuid The UUID of the service to be deleted. * @return Number of services left. */ -size_t NimBLEClient::deleteService(const NimBLEUUID &uuid) { - NIMBLE_LOGD(LOG_TAG, ">> deleteService"); +size_t NimBLEClient::deleteService(const NimBLEUUID& uuid) { // Delete the requested service. - for(auto it = m_servicesVector.begin(); it != m_servicesVector.end(); ++it) { - if((*it)->getUUID() == uuid) { + for (auto it = m_svcVec.begin(); it != m_svcVec.end(); ++it) { + if ((*it)->getUUID() == uuid) { delete *it; - m_servicesVector.erase(it); + m_svcVec.erase(it); break; } } - NIMBLE_LOGD(LOG_TAG, "<< deleteService"); - - return m_servicesVector.size(); -} // deleteServices - - -/** - * @brief Connect to the BLE Server. - * @param [in] deleteAttributes If true this will delete any attribute objects this client may already\n - * have created and clears the vectors after successful connection. - * @return True on success. - */ -bool NimBLEClient::connect(bool deleteAttributes) { - return connect(m_peerAddress, deleteAttributes); -} + return m_svcVec.size(); +} // deleteService +# if CONFIG_BT_NIMBLE_ROLE_OBSERVER /** * @brief Connect to an advertising device. - * @param [in] device The device to connect to. + * @param [in] pDevice A pointer to the advertised device instance to connect to. * @param [in] deleteAttributes If true this will delete any attribute objects this client may already\n - * have created and clears the vectors after successful connection. - * @return True on success. + * have created when last connected. + * @param [in] asyncConnect If true, the connection will be made asynchronously and this function will return immediately.\n + * If false, this function will block until the connection is established or the connection attempt times out. + * @param [in] exchangeMTU If true, the client will attempt to exchange MTU with the server after connection.\n + * If false, the client will use the default MTU size and the application will need to call exchangeMTU() later. + * @return true on success. */ -bool NimBLEClient::connect(NimBLEAdvertisedDevice* device, bool deleteAttributes) { - NimBLEAddress address(device->getAddress()); - return connect(address, deleteAttributes); -} - +bool NimBLEClient::connect(const NimBLEAdvertisedDevice* pDevice, bool deleteAttributes, bool asyncConnect, bool exchangeMTU) { + NimBLEAddress address(pDevice->getAddress()); + return connect(address, deleteAttributes, asyncConnect, exchangeMTU); +} // connect +# endif /** - * @brief Connect to the BLE Server. + * @brief Connect to the BLE Server using the address of the last connected device, or the address\n + * passed to the constructor. + * @param [in] deleteAttributes If true this will delete any attribute objects this client may already\n + * have created when last connected. + * @param [in] asyncConnect If true, the connection will be made asynchronously and this function will return immediately.\n + * If false, this function will block until the connection is established or the connection attempt times out. + * @param [in] exchangeMTU If true, the client will attempt to exchange MTU with the server after connection.\n + * If false, the client will use the default MTU size and the application will need to call exchangeMTU() later. + * @return true on success. + */ +bool NimBLEClient::connect(bool deleteAttributes, bool asyncConnect, bool exchangeMTU) { + return connect(m_peerAddress, deleteAttributes, asyncConnect, exchangeMTU); +} // connect + +/** + * @brief Connect to a BLE Server by address. * @param [in] address The address of the server. * @param [in] deleteAttributes If true this will delete any attribute objects this client may already\n - * have created and clears the vectors after successful connection. - * @return True on success. + * have created when last connected. + * @param [in] asyncConnect If true, the connection will be made asynchronously and this function will return immediately.\n + * If false, this function will block until the connection is established or the connection attempt times out. + * @param [in] exchangeMTU If true, the client will attempt to exchange MTU with the server after connection.\n + * If false, the client will use the default MTU size and the application will need to call exchangeMTU() later. + * @return true on success. */ -bool NimBLEClient::connect(const NimBLEAddress &address, bool deleteAttributes) { +bool NimBLEClient::connect(const NimBLEAddress& address, bool deleteAttributes, bool asyncConnect, bool exchangeMTU) { NIMBLE_LOGD(LOG_TAG, ">> connect(%s)", address.toString().c_str()); - if(!NimBLEDevice::m_synced) { + if (!NimBLEDevice::m_synced) { NIMBLE_LOGE(LOG_TAG, "Host reset, wait for sync."); return false; } - if(isConnected() || m_connEstablished || m_pTaskData != nullptr) { - NIMBLE_LOGE(LOG_TAG, "Client busy, connected to %s, id=%d", - std::string(m_peerAddress).c_str(), getConnId()); + if (isConnected()) { + NIMBLE_LOGE(LOG_TAG, "Client already connected"); return false; } - ble_addr_t peerAddr_t; - memcpy(&peerAddr_t.val, address.getNative(),6); - peerAddr_t.type = address.getType(); - if(ble_gap_conn_find_by_addr(&peerAddr_t, NULL) == 0) { - NIMBLE_LOGE(LOG_TAG, "A connection to %s already exists", - address.toString().c_str()); + const ble_addr_t* peerAddr = address.getBase(); + if (ble_gap_conn_find_by_addr(peerAddr, NULL) == 0) { + NIMBLE_LOGE(LOG_TAG, "A connection to %s already exists", address.toString().c_str()); return false; } - if(address == NimBLEAddress("")) { - NIMBLE_LOGE(LOG_TAG, "Invalid peer address;(NULL)"); + if (address.isNull()) { + NIMBLE_LOGE(LOG_TAG, "Invalid peer address; (NULL)"); return false; } else { m_peerAddress = address; } - TaskHandle_t cur_task = xTaskGetCurrentTaskHandle(); - ble_task_data_t taskData = {this, cur_task, 0, nullptr}; - m_pTaskData = &taskData; - int rc = 0; + if (deleteAttributes) { + deleteServices(); + } + + int rc = 0; + m_config.asyncConnect = asyncConnect; + m_config.exchangeMTU = exchangeMTU; - /* Try to connect the the advertiser. Allow 30 seconds (30000 ms) for - * timeout (default value of m_connectTimeout). - * Loop on BLE_HS_EBUSY if the scan hasn't stopped yet. - */ do { -#if CONFIG_BT_NIMBLE_EXT_ADV - rc = ble_gap_ext_connect(NimBLEDevice::m_own_addr_type, - &peerAddr_t, +# if CONFIG_BT_NIMBLE_EXT_ADV + rc = ble_gap_ext_connect(NimBLEDevice::m_ownAddrType, + peerAddr, m_connectTimeout, m_phyMask, - &m_pConnParams, - &m_pConnParams, - &m_pConnParams, + &m_connParams, + &m_connParams, + &m_connParams, NimBLEClient::handleGapEvent, this); -#else - rc = ble_gap_connect(NimBLEDevice::m_own_addr_type, &peerAddr_t, - m_connectTimeout, &m_pConnParams, - NimBLEClient::handleGapEvent, this); -#endif +# else + rc = ble_gap_connect(NimBLEDevice::m_ownAddrType, + peerAddr, + m_connectTimeout, + &m_connParams, + NimBLEClient::handleGapEvent, + this); +# endif switch (rc) { case 0: break; case BLE_HS_EBUSY: - // Scan was still running, stop it and try again +# if CONFIG_BT_NIMBLE_ROLE_OBSERVER + + // Scan was active, stop it through the NimBLEScan API to release any tasks and call the callback. if (!NimBLEDevice::getScan()->stop()) { rc = BLE_HS_EUNKNOWN; } +# else + rc = BLE_HS_EUNKNOWN; +# endif break; case BLE_HS_EDONE: // A connection to this device already exists, do not connect twice. - NIMBLE_LOGE(LOG_TAG, "Already connected to device; addr=%s", - std::string(m_peerAddress).c_str()); + NIMBLE_LOGE(LOG_TAG, "Already connected to device; addr=%s", std::string(m_peerAddress).c_str()); break; case BLE_HS_EALREADY: - // Already attempting to connect to this device, cancel the previous - // attempt and report failure here so we don't get 2 connections. - NIMBLE_LOGE(LOG_TAG, "Already attempting to connect to %s - cancelling", - std::string(m_peerAddress).c_str()); - ble_gap_conn_cancel(); + NIMBLE_LOGE(LOG_TAG, "Already attempting to connect"); break; default: - NIMBLE_LOGE(LOG_TAG, "Failed to connect to %s, rc=%d; %s", + NIMBLE_LOGE(LOG_TAG, + "Failed to connect to %s, rc=%d; %s", std::string(m_peerAddress).c_str(), - rc, NimBLEUtils::returnCodeToString(rc)); + rc, + NimBLEUtils::returnCodeToString(rc)); break; } } while (rc == BLE_HS_EBUSY); - m_lastErr = rc; - - if(rc != 0) { - m_pTaskData = nullptr; + if (rc != 0) { + m_lastErr = rc; return false; } -#ifdef ulTaskNotifyValueClear - // Clear the task notification value to ensure we block - ulTaskNotifyValueClear(cur_task, ULONG_MAX); -#endif + if (m_config.asyncConnect) { + return true; + } + + NimBLETaskData taskData(this); + m_pTaskData = &taskData; + // Wait for the connect timeout time +1 second for the connection to complete - if(ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(m_connectTimeout + 1000)) == pdFALSE) { - m_pTaskData = nullptr; - // If a connection was made but no response from MTU exchange; disconnect - if(isConnected()) { - NIMBLE_LOGE(LOG_TAG, "Connect timeout - no response"); - disconnect(); + if (!NimBLEUtils::taskWait(taskData, m_connectTimeout + 1000)) { + // If a connection was made but no response from MTU exchange proceed anyway + if (isConnected()) { + taskData.m_flags = 0; } else { - // workaround; if the controller doesn't cancel the connection - // at the timeout, cancel it here. + // workaround; if the controller doesn't cancel the connection at the timeout, cancel it here. NIMBLE_LOGE(LOG_TAG, "Connect timeout - cancelling"); ble_gap_conn_cancel(); + taskData.m_flags = BLE_HS_ETIMEOUT; } - - return false; - - } else if(taskData.rc != 0){ - m_lastErr = taskData.rc; - NIMBLE_LOGE(LOG_TAG, "Connection failed; status=%d %s", - taskData.rc, - NimBLEUtils::returnCodeToString(taskData.rc)); - // If the failure was not a result of a disconnection - // make sure we disconnect now to avoid dangling connections - if(isConnected()) { - disconnect(); - } - return false; - } else { - NIMBLE_LOGI(LOG_TAG, "Connection established"); } - if(deleteAttributes) { - deleteServices(); + m_pTaskData = nullptr; + rc = taskData.m_flags; + if (rc != 0) { + NIMBLE_LOGE(LOG_TAG, "Connection failed; status=%d %s", rc, NimBLEUtils::returnCodeToString(rc)); + m_lastErr = rc; + if (m_config.deleteOnConnectFail) { + NimBLEDevice::deleteClient(this); + } + return false; } - m_connEstablished = true; m_pClientCallbacks->onConnect(this); - NIMBLE_LOGD(LOG_TAG, "<< connect()"); // Check if still connected before returning return isConnected(); } // connect - /** * @brief Initiate a secure connection (pair/bond) with the server.\n * Called automatically when a characteristic or descriptor requires encryption or authentication to access it. + * @param [in] async If true, the connection will be secured asynchronously and this function will return immediately.\n + * If false, this function will block until the connection is secured or the client disconnects. * @return True on success. + * @details If async=false, this function will block and should not be used in a callback. */ -bool NimBLEClient::secureConnection() { +bool NimBLEClient::secureConnection(bool async) const { NIMBLE_LOGD(LOG_TAG, ">> secureConnection()"); - TaskHandle_t cur_task = xTaskGetCurrentTaskHandle(); - ble_task_data_t taskData = {this, cur_task, 0, nullptr}; - int retryCount = 1; - - do { - m_pTaskData = &taskData; - - int rc = NimBLEDevice::startSecurity(m_conn_id); - if(rc != 0 && rc != BLE_HS_EALREADY){ - m_lastErr = rc; - m_pTaskData = nullptr; - return false; - } - -#ifdef ulTaskNotifyValueClear - // Clear the task notification value to ensure we block - ulTaskNotifyValueClear(cur_task, ULONG_MAX); -#endif - ulTaskNotifyTake(pdTRUE, portMAX_DELAY); - } while (taskData.rc == (BLE_HS_ERR_HCI_BASE + BLE_ERR_PINKEY_MISSING) && retryCount--); - - if(taskData.rc != 0){ - m_lastErr = taskData.rc; - NIMBLE_LOGE(LOG_TAG, "secureConnection: failed rc=%d", taskData.rc); + int rc = 0; + if (async && !NimBLEDevice::startSecurity(m_connHandle, &rc)) { + m_lastErr = rc; + m_asyncSecureAttempt = 0; return false; } - NIMBLE_LOGD(LOG_TAG, "<< secureConnection: success"); - return true; -} // secureConnection + if (async) { + m_asyncSecureAttempt++; + return true; + } + NimBLETaskData taskData(const_cast(this), BLE_HS_ENOTCONN); + m_pTaskData = &taskData; + int retryCount = 1; + do { + if (NimBLEDevice::startSecurity(m_connHandle)) { + NimBLEUtils::taskWait(taskData, BLE_NPL_TIME_FOREVER); + } + } while (taskData.m_flags == (BLE_HS_ERR_HCI_BASE + BLE_ERR_PINKEY_MISSING) && retryCount--); + + m_pTaskData = nullptr; + + if (taskData.m_flags == 0) { + NIMBLE_LOGD(LOG_TAG, "<< secureConnection: success"); + return true; + } + + m_lastErr = taskData.m_flags; + NIMBLE_LOGE(LOG_TAG, "secureConnection: failed rc=%d", taskData.m_flags); + return false; + +} // secureConnection /** * @brief Disconnect from the peer. - * @return Error code from NimBLE stack, 0 = success. + * @return True if the command was successfully sent. */ -int NimBLEClient::disconnect(uint8_t reason) { - NIMBLE_LOGD(LOG_TAG, ">> disconnect()"); - int rc = 0; - if(isConnected()) { - // If the timer was already started, ignore this call. - if(ble_npl_callout_is_active(&m_dcTimer)) { - NIMBLE_LOGI(LOG_TAG, "Already disconnecting, timer started"); - return BLE_HS_EALREADY; - } - - ble_gap_conn_desc desc; - if(ble_gap_conn_find(m_conn_id, &desc) != 0){ - NIMBLE_LOGI(LOG_TAG, "Connection ID not found"); - return BLE_HS_EALREADY; - } - - // We use a timer to detect a controller error in the event that it does - // not inform the stack when disconnection is complete. - // This is a common error in certain esp-idf versions. - // The disconnect timeout time is the supervision timeout time + 1 second. - // In the case that the event happens shortly after the supervision timeout - // we don't want to prematurely reset the host. - ble_npl_time_t ticks; - ble_npl_time_ms_to_ticks((desc.supervision_timeout + 100) * 10, &ticks); - ble_npl_callout_reset(&m_dcTimer, ticks); - - rc = ble_gap_terminate(m_conn_id, reason); - if (rc != 0) { - if(rc != BLE_HS_EALREADY) { - ble_npl_callout_stop(&m_dcTimer); - } - NIMBLE_LOGE(LOG_TAG, "ble_gap_terminate failed: rc=%d %s", - rc, NimBLEUtils::returnCodeToString(rc)); - } - } else { - NIMBLE_LOGD(LOG_TAG, "Not connected to any peers"); +bool NimBLEClient::disconnect(uint8_t reason) { + int rc = ble_gap_terminate(m_connHandle, reason); + if (rc != 0 && rc != BLE_HS_ENOTCONN && rc != BLE_HS_EALREADY) { + NIMBLE_LOGE(LOG_TAG, "ble_gap_terminate failed: rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc)); + m_lastErr = rc; + return false; } - NIMBLE_LOGD(LOG_TAG, "<< disconnect()"); - m_lastErr = rc; - return rc; + return true; } // disconnect +/** + * @brief Cancel an ongoing connection attempt. + * @return True if the command was successfully sent. + */ +bool NimBLEClient::cancelConnect() const { + int rc = ble_gap_conn_cancel(); + if (rc != 0 && rc != BLE_HS_EALREADY) { + NIMBLE_LOGE(LOG_TAG, "ble_gap_conn_cancel failed: rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc)); + m_lastErr = rc; + return false; + } -#if CONFIG_BT_NIMBLE_EXT_ADV + return true; +} // cancelConnect + +/** + * @brief Set or unset a flag to delete this client when disconnected or connection failed. + * @param [in] deleteOnDisconnect Set the client to self delete when disconnected. + * @param [in] deleteOnConnectFail Set the client to self delete when a connection attempt fails. + */ +void NimBLEClient::setSelfDelete(bool deleteOnDisconnect, bool deleteOnConnectFail) { + m_config.deleteOnDisconnect = deleteOnDisconnect; + m_config.deleteOnConnectFail = deleteOnConnectFail; +} // setSelfDelete + +/** + * @brief Get a copy of the clients configuration. + * @return A copy of the clients configuration. + */ +NimBLEClient::Config NimBLEClient::getConfig() const { + return m_config; +} // getConfig + +/** + * @brief Set the client configuration options. + * @param [in] config The config options instance to set the client configuration to. + */ +void NimBLEClient::setConfig(NimBLEClient::Config config) { + m_config = config; +} // setConfig + +# if CONFIG_BT_NIMBLE_EXT_ADV /** * @brief Set the PHY types to use when connecting to a server. * @param [in] mask A bitmask indicating what PHYS to connect with.\n @@ -429,9 +413,50 @@ int NimBLEClient::disconnect(uint8_t reason) { */ void NimBLEClient::setConnectPhy(uint8_t mask) { m_phyMask = mask; -} -#endif +} // setConnectPhy +# endif +/** + * @brief Request a change to the PHY used for this peer connection. + * @param [in] txPhyMask TX PHY. Can be mask of following: + * - BLE_GAP_LE_PHY_1M_MASK + * - BLE_GAP_LE_PHY_2M_MASK + * - BLE_GAP_LE_PHY_CODED_MASK + * - BLE_GAP_LE_PHY_ANY_MASK + * @param [in] rxPhyMask RX PHY. Can be mask of following: + * - BLE_GAP_LE_PHY_1M_MASK + * - BLE_GAP_LE_PHY_2M_MASK + * - BLE_GAP_LE_PHY_CODED_MASK + * - BLE_GAP_LE_PHY_ANY_MASK + * @param phyOptions Additional PHY options. Valid values are: + * - BLE_GAP_LE_PHY_CODED_ANY (default) + * - BLE_GAP_LE_PHY_CODED_S2 + * - BLE_GAP_LE_PHY_CODED_S8 + * @return True if successful. + */ +bool NimBLEClient::updatePhy(uint8_t txPhyMask, uint8_t rxPhyMask, uint16_t phyOptions) { + int rc = ble_gap_set_prefered_le_phy(m_connHandle, txPhyMask, rxPhyMask, phyOptions); + if (rc != 0) { + NIMBLE_LOGE(LOG_TAG, "Failed to update phy; rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc)); + } + + return rc == 0; +} // updatePhy + +/** + * @brief Get the PHY used for this peer connection. + * @param [out] txPhy The TX PHY. + * @param [out] rxPhy The RX PHY. + * @return True if successful. + */ +bool NimBLEClient::getPhy(uint8_t* txPhy, uint8_t* rxPhy) { + int rc = ble_gap_read_le_phy(m_connHandle, txPhy, rxPhy); + if (rc != 0) { + NIMBLE_LOGE(LOG_TAG, "Failed to read phy; rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc)); + } + + return rc == 0; +} // getPhy /** * @brief Set the connection parameters to use when connecting to a server. @@ -442,25 +467,22 @@ void NimBLEClient::setConnectPhy(uint8_t mask) { * @param [in] scanInterval The scan interval to use when attempting to connect in 0.625ms units. * @param [in] scanWindow The scan window to use when attempting to connect in 0.625ms units. */ -void NimBLEClient::setConnectionParams(uint16_t minInterval, uint16_t maxInterval, - uint16_t latency, uint16_t timeout, - uint16_t scanInterval, uint16_t scanWindow)/*, - uint16_t minConnTime, uint16_t maxConnTime)*/ +void NimBLEClient::setConnectionParams( + uint16_t minInterval, uint16_t maxInterval, uint16_t latency, uint16_t timeout, uint16_t scanInterval, uint16_t scanWindow) +/*, uint16_t minConnEvtTime, uint16_t maxConnEvtTime)*/ { - - m_pConnParams.scan_itvl = scanInterval; - m_pConnParams.scan_window = scanWindow; - m_pConnParams.itvl_min = minInterval; - m_pConnParams.itvl_max = maxInterval; - m_pConnParams.latency = latency; - m_pConnParams.supervision_timeout = timeout; + m_connParams.itvl_min = minInterval; + m_connParams.itvl_max = maxInterval; + m_connParams.latency = latency; + m_connParams.supervision_timeout = timeout; + m_connParams.scan_itvl = scanInterval; + m_connParams.scan_window = scanWindow; // These are not used by NimBLE at this time - Must leave at defaults - //m_pConnParams->min_ce_len = minConnTime; // Minimum length of connection event in 0.625ms units - //m_pConnParams->max_ce_len = maxConnTime; // Maximum length of connection event in 0.625ms units + // m_connParams.min_ce_len = minConnEvtTime; // Minimum length of connection event in 0.625ms units + // m_connParams.max_ce_len = maxConnEvtTime; // Maximum length of connection event in 0.625ms units } // setConnectionParams - /** * @brief Update the connection parameters: * * Can only be used after a connection has been established. @@ -469,26 +491,23 @@ void NimBLEClient::setConnectionParams(uint16_t minInterval, uint16_t maxInterva * @param [in] latency The number of packets allowed to skip (extends max interval). * @param [in] timeout The timeout time in 10ms units before disconnecting. */ -void NimBLEClient::updateConnParams(uint16_t minInterval, uint16_t maxInterval, - uint16_t latency, uint16_t timeout) -{ - ble_gap_upd_params params; +bool NimBLEClient::updateConnParams(uint16_t minInterval, uint16_t maxInterval, uint16_t latency, uint16_t timeout) { + ble_gap_upd_params params{.itvl_min = minInterval, + .itvl_max = maxInterval, + .latency = latency, + .supervision_timeout = timeout, + // These are not used by NimBLE at this time - leave at defaults + .min_ce_len = BLE_GAP_INITIAL_CONN_MIN_CE_LEN, + .max_ce_len = BLE_GAP_INITIAL_CONN_MAX_CE_LEN}; - params.latency = latency; - params.itvl_max = maxInterval; - params.itvl_min = minInterval; - params.supervision_timeout = timeout; - // These are not used by NimBLE at this time - Must leave at defaults - params.min_ce_len = BLE_GAP_INITIAL_CONN_MIN_CE_LEN; - params.max_ce_len = BLE_GAP_INITIAL_CONN_MAX_CE_LEN; - - int rc = ble_gap_update_params(m_conn_id, ¶ms); - if(rc != 0) { - NIMBLE_LOGE(LOG_TAG, "Update params error: %d, %s", - rc, NimBLEUtils::returnCodeToString(rc)); + int rc = ble_gap_update_params(m_connHandle, ¶ms); + if (rc != 0) { + NIMBLE_LOGE(LOG_TAG, "Update params error: %d, %s", rc, NimBLEUtils::returnCodeToString(rc)); + m_lastErr = rc; } -} // updateConnParams + return rc == 0; +} // updateConnParams /** * @brief Request an update of the data packet length. @@ -496,184 +515,111 @@ void NimBLEClient::updateConnParams(uint16_t minInterval, uint16_t maxInterval, * @details Sends a data length update request to the server the client is connected to. * The Data Length Extension (DLE) allows to increase the Data Channel Payload from 27 bytes to up to 251 bytes. * The server needs to support the Bluetooth 4.2 specifications, to be capable of DLE. - * @param [in] tx_octets The preferred number of payload octets to use (Range 0x001B-0x00FB). + * @param [in] txOctets The preferred number of payload octets to use (Range 0x001B-0x00FB). */ -void NimBLEClient::setDataLen(uint16_t tx_octets) { -#if defined(CONFIG_NIMBLE_CPP_IDF) && !defined(ESP_IDF_VERSION) || \ - (ESP_IDF_VERSION_MAJOR * 100 + ESP_IDF_VERSION_MINOR * 10 + ESP_IDF_VERSION_PATCH) < 432 - return; -#else - uint16_t tx_time = (tx_octets + 14) * 8; - - int rc = ble_gap_set_data_len(m_conn_id, tx_octets, tx_time); - if(rc != 0) { +bool NimBLEClient::setDataLen(uint16_t txOctets) { +# if defined(CONFIG_NIMBLE_CPP_IDF) && !defined(ESP_IDF_VERSION) || \ + (ESP_IDF_VERSION_MAJOR * 100 + ESP_IDF_VERSION_MINOR * 10 + ESP_IDF_VERSION_PATCH) < 432 + return false; +# else + uint16_t txTime = (txOctets + 14) * 8; + int rc = ble_gap_set_data_len(m_connHandle, txOctets, txTime); + if (rc != 0) { NIMBLE_LOGE(LOG_TAG, "Set data length error: %d, %s", rc, NimBLEUtils::returnCodeToString(rc)); } -#endif -} // setDataLen + return rc == 0; +# endif +} // setDataLen /** * @brief Get detailed information about the current peer connection. + * @return A NimBLEConnInfo instance with the data, or a NULL instance if not found. */ -NimBLEConnInfo NimBLEClient::getConnInfo() { - NimBLEConnInfo connInfo; - if (!isConnected()) { - NIMBLE_LOGE(LOG_TAG, "Not connected"); - } else { - int rc = ble_gap_conn_find(m_conn_id, &connInfo.m_desc); - if (rc != 0) { - NIMBLE_LOGE(LOG_TAG, "Connection info not found"); - } +NimBLEConnInfo NimBLEClient::getConnInfo() const { + NimBLEConnInfo connInfo{}; + if (ble_gap_conn_find(m_connHandle, &connInfo.m_desc) != 0) { + NIMBLE_LOGE(LOG_TAG, "Connection info not found"); } return connInfo; } // getConnInfo - /** * @brief Set the timeout to wait for connection attempt to complete. - * @param [in] time The number of milliseconds before timeout. + * @param [in] time The number of milliseconds before timeout, default is 30 seconds. */ void NimBLEClient::setConnectTimeout(uint32_t time) { m_connectTimeout = time; } // setConnectTimeout - /** - * @brief Get the connection id for this client. - * @return The connection id. + * @brief Get the connection handle for this client. + * @return The connection handle. */ -uint16_t NimBLEClient::getConnId() { - return m_conn_id; -} // getConnId - -/** - * @brief Clear the connection information for this client. - * @note This is designed to be used to reset the connection information after - * calling setConnection(), and should not be used to disconnect from a - * peer. To disconnect from a peer, use disconnect(). - */ -void NimBLEClient::clearConnection() { - m_conn_id = BLE_HS_CONN_HANDLE_NONE; - m_connEstablished = false; - m_peerAddress = NimBLEAddress(); -} // clearConnection - -/** - * @brief Set the connection information for this client. - * @param [in] connInfo The connection information. - * @return True on success. - * @note Sets the connection established flag to true. - * @note If the client is already connected to a peer, this will return false. - * @note This is designed to be used when a connection is made outside of the - * NimBLEClient class, such as when a connection is made by the - * NimBLEServer class and the client is passed the connection id. This use - * enables the GATT Server to read the name of the device that has - * connected to it. - */ -bool NimBLEClient::setConnection(NimBLEConnInfo &connInfo) { - if (isConnected() || m_connEstablished) { - NIMBLE_LOGE(LOG_TAG, "Already connected"); - return false; - } - - m_peerAddress = connInfo.getAddress(); - m_conn_id = connInfo.getConnHandle(); - m_connEstablished = true; - - return true; -} // setConnection - -/** - * @brief Set the connection information for this client. - * @param [in] conn_id The connection id. - * @note Sets the connection established flag to true. - * @note This is designed to be used when a connection is made outside of the - * NimBLEClient class, such as when a connection is made by the - * NimBLEServer class and the client is passed the connection id. This use - * enables the GATT Server to read the name of the device that has - * connected to it. - * @note If the client is already connected to a peer, this will return false. - * @note This will look up the peer address using the connection id. - */ -bool NimBLEClient::setConnection(uint16_t conn_id) { - // we weren't provided the peer address, look it up using ble_gap_conn_find - NimBLEConnInfo connInfo; - int rc = ble_gap_conn_find(m_conn_id, &connInfo.m_desc); - if (rc != 0) { - NIMBLE_LOGE(LOG_TAG, "Connection info not found"); - return false; - } - - return setConnection(connInfo); -} // setConnection +uint16_t NimBLEClient::getConnHandle() const { + return m_connHandle; +} // getConnHandle /** * @brief Retrieve the address of the peer. + * @return A NimBLEAddress instance with the peer address data. */ -NimBLEAddress NimBLEClient::getPeerAddress() { +NimBLEAddress NimBLEClient::getPeerAddress() const { return m_peerAddress; } // getPeerAddress - /** * @brief Set the peer address. - * @param [in] address The address of the peer that this client is - * connected or should connect to. + * @param [in] address The address of the peer that this client is connected or should connect to. + * @return True if successful. */ -void NimBLEClient::setPeerAddress(const NimBLEAddress &address) { - if(isConnected()) { +bool NimBLEClient::setPeerAddress(const NimBLEAddress& address) { + if (isConnected()) { NIMBLE_LOGE(LOG_TAG, "Cannot set peer address while connected"); - return; + return false; } m_peerAddress = address; - NIMBLE_LOGD(LOG_TAG, "Peer address set: %s", std::string(m_peerAddress).c_str()); + return true; } // setPeerAddress - /** * @brief Ask the BLE server for the RSSI value. - * @return The RSSI value. + * @return The RSSI value or 0 if there was an error. */ -int NimBLEClient::getRssi() { - NIMBLE_LOGD(LOG_TAG, ">> getRssi()"); +int NimBLEClient::getRssi() const { if (!isConnected()) { - NIMBLE_LOGE(LOG_TAG, "<< getRssi(): Not connected"); + NIMBLE_LOGE(LOG_TAG, "getRssi(): Not connected"); return 0; } - int8_t rssiValue = 0; - int rc = ble_gap_conn_rssi(m_conn_id, &rssiValue); - if(rc != 0) { - NIMBLE_LOGE(LOG_TAG, "Failed to read RSSI error code: %d, %s", - rc, NimBLEUtils::returnCodeToString(rc)); + int8_t rssi = 0; + int rc = ble_gap_conn_rssi(m_connHandle, &rssi); + if (rc != 0) { + NIMBLE_LOGE(LOG_TAG, "Failed to read RSSI error code: %d, %s", rc, NimBLEUtils::returnCodeToString(rc)); m_lastErr = rc; return 0; } - return rssiValue; + return rssi; } // getRssi - /** * @brief Get iterator to the beginning of the vector of remote service pointers. * @return An iterator to the beginning of the vector of remote service pointers. */ std::vector::iterator NimBLEClient::begin() { - return m_servicesVector.begin(); -} - + return m_svcVec.begin(); +} // begin /** * @brief Get iterator to the end of the vector of remote service pointers. * @return An iterator to the end of the vector of remote service pointers. */ std::vector::iterator NimBLEClient::end() { - return m_servicesVector.end(); -} - + return m_svcVec.end(); +} // end /** * @brief Get the service BLE Remote Service instance corresponding to the uuid. @@ -684,38 +630,35 @@ NimBLERemoteService* NimBLEClient::getService(const char* uuid) { return getService(NimBLEUUID(uuid)); } // getService - /** * @brief Get the service object corresponding to the uuid. * @param [in] uuid The UUID of the service being sought. * @return A pointer to the service or nullptr if not found. */ -NimBLERemoteService* NimBLEClient::getService(const NimBLEUUID &uuid) { +NimBLERemoteService* NimBLEClient::getService(const NimBLEUUID& uuid) { NIMBLE_LOGD(LOG_TAG, ">> getService: uuid: %s", uuid.toString().c_str()); - for(auto &it: m_servicesVector) { - if(it->getUUID() == uuid) { + for (auto& it : m_svcVec) { + if (it->getUUID() == uuid) { NIMBLE_LOGD(LOG_TAG, "<< getService: found the service with uuid: %s", uuid.toString().c_str()); return it; } } - size_t prev_size = m_servicesVector.size(); - if(retrieveServices(&uuid)) { - if(m_servicesVector.size() > prev_size) { - return m_servicesVector.back(); + size_t prevSize = m_svcVec.size(); + if (retrieveServices(&uuid)) { + if (m_svcVec.size() > prevSize) { + return m_svcVec.back(); } // If the request was successful but 16/32 bit uuid not found // try again with the 128 bit uuid. - if(uuid.bitSize() == BLE_UUID_TYPE_16 || - uuid.bitSize() == BLE_UUID_TYPE_32) - { + if (uuid.bitSize() == BLE_UUID_TYPE_16 || uuid.bitSize() == BLE_UUID_TYPE_32) { NimBLEUUID uuid128(uuid); uuid128.to128(); - if(retrieveServices(&uuid128)) { - if(m_servicesVector.size() > prev_size) { - return m_servicesVector.back(); + if (retrieveServices(&uuid128)) { + if (m_svcVec.size() > prevSize) { + return m_svcVec.back(); } } } else { @@ -725,9 +668,9 @@ NimBLERemoteService* NimBLEClient::getService(const NimBLEUUID &uuid) { uuid16.to16(); // if the uuid was 128 bit but not of the BLE base type this check will fail if (uuid16.bitSize() == BLE_UUID_TYPE_16) { - if(retrieveServices(&uuid16)) { - if(m_servicesVector.size() > prev_size) { - return m_servicesVector.back(); + if (retrieveServices(&uuid16)) { + if (m_svcVec.size() > prevSize) { + return m_svcVec.back(); } } } @@ -738,7 +681,6 @@ NimBLERemoteService* NimBLEClient::getService(const NimBLEUUID &uuid) { return nullptr; } // getService - /** * @brief Get a pointer to the vector of found services. * @param [in] refresh If true the current services vector will be cleared and\n @@ -746,20 +688,18 @@ NimBLERemoteService* NimBLEClient::getService(const NimBLEUUID &uuid) { * If false the vector will be returned with the currently stored services. * @return A pointer to the vector of available services. */ -std::vector* NimBLEClient::getServices(bool refresh) { - if(refresh) { +const std::vector& NimBLEClient::getServices(bool refresh) { + if (refresh) { deleteServices(); - if (!retrieveServices()) { NIMBLE_LOGE(LOG_TAG, "Error: Failed to get services"); - } - else{ - NIMBLE_LOGI(LOG_TAG, "Found %d services", m_servicesVector.size()); + } else { + NIMBLE_LOGI(LOG_TAG, "Found %d services", m_svcVec.size()); } } - return &m_servicesVector; -} // getServices + return m_svcVec; +} // getServices /** * @brief Retrieves the full database of attributes that the peripheral has available. @@ -767,18 +707,16 @@ std::vector* NimBLEClient::getServices(bool refresh) { */ bool NimBLEClient::discoverAttributes() { deleteServices(); - - if (!retrieveServices()){ + if (!retrieveServices()) { return false; } - - for(auto svc: m_servicesVector) { + for (auto svc : m_svcVec) { if (!svc->retrieveCharacteristics()) { return false; } - for(auto chr: svc->m_characteristicVector) { + for (auto chr : svc->m_vChars) { if (!chr->retrieveDescriptors()) { return false; } @@ -788,36 +726,24 @@ bool NimBLEClient::discoverAttributes() { return true; } // discoverAttributes - /** - * @brief Ask the remote %BLE server for its services.\n - * Here we ask the server for its set of services and wait until we have received them all. + * @brief Ask the remote BLE server for its services. + * * Here we ask the server for its set of services and wait until we have received them all. * @return true on success otherwise false if an error occurred */ -bool NimBLEClient::retrieveServices(const NimBLEUUID *uuid_filter) { -/** - * Design - * ------ - * We invoke ble_gattc_disc_all_svcs. This will request a list of the services exposed by the - * peer BLE partner to be returned in the callback function provided. - */ - - NIMBLE_LOGD(LOG_TAG, ">> retrieveServices"); - - if(!isConnected()){ +bool NimBLEClient::retrieveServices(const NimBLEUUID* uuidFilter) { + if (!isConnected()) { NIMBLE_LOGE(LOG_TAG, "Disconnected, could not retrieve services -aborting"); return false; } - int rc = 0; - TaskHandle_t cur_task = xTaskGetCurrentTaskHandle(); - ble_task_data_t taskData = {this, cur_task, 0, nullptr}; + int rc = 0; + NimBLETaskData taskData(this); - if(uuid_filter == nullptr) { - rc = ble_gattc_disc_all_svcs(m_conn_id, NimBLEClient::serviceDiscoveredCB, &taskData); + if (uuidFilter == nullptr) { + rc = ble_gattc_disc_all_svcs(m_connHandle, NimBLEClient::serviceDiscoveredCB, &taskData); } else { - rc = ble_gattc_disc_svc_by_uuid(m_conn_id, &uuid_filter->getNative()->u, - NimBLEClient::serviceDiscoveredCB, &taskData); + rc = ble_gattc_disc_svc_by_uuid(m_connHandle, uuidFilter->getBase(), NimBLEClient::serviceDiscoveredCB, &taskData); } if (rc != 0) { @@ -826,86 +752,73 @@ bool NimBLEClient::retrieveServices(const NimBLEUUID *uuid_filter) { return false; } -#ifdef ulTaskNotifyValueClear - // Clear the task notification value to ensure we block - ulTaskNotifyValueClear(cur_task, ULONG_MAX); -#endif - - // wait until we have all the services - ulTaskNotifyTake(pdTRUE, portMAX_DELAY); - m_lastErr = taskData.rc; - - if(taskData.rc == 0){ - NIMBLE_LOGD(LOG_TAG, "<< retrieveServices"); + NimBLEUtils::taskWait(taskData, BLE_NPL_TIME_FOREVER); + rc = taskData.m_flags; + if (rc == 0 || rc == BLE_HS_EDONE) { return true; } - else { - NIMBLE_LOGE(LOG_TAG, "Could not retrieve services"); - return false; - } + + m_lastErr = rc; + NIMBLE_LOGE(LOG_TAG, "Could not retrieve services, rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc)); + return false; } // getServices - /** - * @brief STATIC Callback for the service discovery API function.\n - * When a service is found or there is none left or there was an error + * @brief Callback for the service discovery API function. + * @details When a service is found or there is none left or there was an error * the API will call this and report findings. */ -int NimBLEClient::serviceDiscoveredCB( - uint16_t conn_handle, - const struct ble_gatt_error *error, - const struct ble_gatt_svc *service, void *arg) -{ - NIMBLE_LOGD(LOG_TAG,"Service Discovered >> status: %d handle: %d", - error->status, (error->status == 0) ? service->start_handle : -1); +int NimBLEClient::serviceDiscoveredCB(uint16_t connHandle, + const struct ble_gatt_error* error, + const struct ble_gatt_svc* service, + void* arg) { + NIMBLE_LOGD(LOG_TAG, + "Service Discovered >> status: %d handle: %d", + error->status, + (error->status == 0) ? service->start_handle : -1); - ble_task_data_t *pTaskData = (ble_task_data_t*)arg; - NimBLEClient *client = (NimBLEClient*)pTaskData->pATT; + NimBLETaskData* pTaskData = (NimBLETaskData*)arg; + NimBLEClient* pClient = (NimBLEClient*)pTaskData->m_pInstance; + + if (error->status == BLE_HS_ENOTCONN) { + NIMBLE_LOGE(LOG_TAG, "<< Service Discovered; Disconnected"); + NimBLEUtils::taskRelease(*pTaskData, error->status); + return error->status; + } // Make sure the service discovery is for this device - if(client->getConnId() != conn_handle){ + if (pClient->getConnHandle() != connHandle) { return 0; } - if(error->status == 0) { + if (error->status == 0) { // Found a service - add it to the vector - NimBLERemoteService* pRemoteService = new NimBLERemoteService(client, service); - client->m_servicesVector.push_back(pRemoteService); + pClient->m_svcVec.push_back(new NimBLERemoteService(pClient, service)); return 0; } - if(error->status == BLE_HS_EDONE) { - pTaskData->rc = 0; - } else { - NIMBLE_LOGE(LOG_TAG, "serviceDiscoveredCB() rc=%d %s", - error->status, - NimBLEUtils::returnCodeToString(error->status)); - pTaskData->rc = error->status; - } - - xTaskNotifyGive(pTaskData->task); - - NIMBLE_LOGD(LOG_TAG,"<< Service Discovered"); + NimBLEUtils::taskRelease(*pTaskData, error->status); + NIMBLE_LOGD(LOG_TAG, "<< Service Discovered"); return error->status; -} - +} // serviceDiscoveredCB /** * @brief Get the value of a specific characteristic associated with a specific service. * @param [in] serviceUUID The service that owns the characteristic. * @param [in] characteristicUUID The characteristic whose value we wish to read. - * @returns characteristic value or an empty string if not found + * @returns characteristic value or an empty value if not found. */ -NimBLEAttValue NimBLEClient::getValue(const NimBLEUUID &serviceUUID, const NimBLEUUID &characteristicUUID) { - NIMBLE_LOGD(LOG_TAG, ">> getValue: serviceUUID: %s, characteristicUUID: %s", - serviceUUID.toString().c_str(), characteristicUUID.toString().c_str()); +NimBLEAttValue NimBLEClient::getValue(const NimBLEUUID& serviceUUID, const NimBLEUUID& characteristicUUID) { + NIMBLE_LOGD(LOG_TAG, + ">> getValue: serviceUUID: %s, characteristicUUID: %s", + serviceUUID.toString().c_str(), + characteristicUUID.toString().c_str()); - NimBLEAttValue ret; - NimBLERemoteService* pService = getService(serviceUUID); - - if(pService != nullptr) { - NimBLERemoteCharacteristic* pChar = pService->getCharacteristic(characteristicUUID); - if(pChar != nullptr) { + NimBLEAttValue ret{}; + auto pService = getService(serviceUUID); + if (pService != nullptr) { + auto pChar = pService->getCharacteristic(characteristicUUID); + if (pChar != nullptr) { ret = pChar->readValue(); } } @@ -914,7 +827,6 @@ NimBLEAttValue NimBLEClient::getValue(const NimBLEUUID &serviceUUID, const NimBL return ret; } // getValue - /** * @brief Set the value of a specific characteristic associated with a specific service. * @param [in] serviceUUID The service that owns the characteristic. @@ -923,18 +835,20 @@ NimBLEAttValue NimBLEClient::getValue(const NimBLEUUID &serviceUUID, const NimBL * @param [in] response If true, uses write with response operation. * @returns true if successful otherwise false */ -bool NimBLEClient::setValue(const NimBLEUUID &serviceUUID, const NimBLEUUID &characteristicUUID, - const NimBLEAttValue &value, bool response) -{ - NIMBLE_LOGD(LOG_TAG, ">> setValue: serviceUUID: %s, characteristicUUID: %s", - serviceUUID.toString().c_str(), characteristicUUID.toString().c_str()); +bool NimBLEClient::setValue(const NimBLEUUID& serviceUUID, + const NimBLEUUID& characteristicUUID, + const NimBLEAttValue& value, + bool response) { + NIMBLE_LOGD(LOG_TAG, + ">> setValue: serviceUUID: %s, characteristicUUID: %s", + serviceUUID.toString().c_str(), + characteristicUUID.toString().c_str()); - bool ret = false; - NimBLERemoteService* pService = getService(serviceUUID); - - if(pService != nullptr) { + bool ret = false; + auto pService = getService(serviceUUID); + if (pService != nullptr) { NimBLERemoteCharacteristic* pChar = pService->getCharacteristic(characteristicUUID); - if(pChar != nullptr) { + if (pChar != nullptr) { ret = pChar->writeValue(value, response); } } @@ -943,61 +857,92 @@ bool NimBLEClient::setValue(const NimBLEUUID &serviceUUID, const NimBLEUUID &cha return ret; } // setValue - /** * @brief Get the remote characteristic with the specified handle. * @param [in] handle The handle of the desired characteristic. * @returns The matching remote characteristic, nullptr otherwise. */ -NimBLERemoteCharacteristic* NimBLEClient::getCharacteristic(const uint16_t handle) -{ - NimBLERemoteService *pService = nullptr; - for(auto it = m_servicesVector.begin(); it != m_servicesVector.end(); ++it) { - if ((*it)->getStartHandle() <= handle && handle <= (*it)->getEndHandle()) { - pService = *it; - break; - } - } - - if (pService != nullptr) { - for (auto it = pService->begin(); it != pService->end(); ++it) { - if ((*it)->getHandle() == handle) { - return *it; +NimBLERemoteCharacteristic* NimBLEClient::getCharacteristic(uint16_t handle) { + for (const auto& svc : m_svcVec) { + if (svc->getStartHandle() <= handle && handle <= svc->getEndHandle()) { + for (const auto& chr : svc->m_vChars) { + if (chr->getHandle() == handle) { + return chr; + } } } } return nullptr; -} +} // getCharacteristic /** * @brief Get the current mtu of this connection. * @returns The MTU value. */ -uint16_t NimBLEClient::getMTU() { - return ble_att_mtu(m_conn_id); +uint16_t NimBLEClient::getMTU() const { + return ble_att_mtu(m_connHandle); } // getMTU +/** + * @brief Callback for the MTU exchange API function. + * @details When the MTU exchange is complete the API will call this and report the new MTU. + */ +int NimBLEClient::exchangeMTUCb(uint16_t conn_handle, const ble_gatt_error* error, uint16_t mtu, void* arg) { + NIMBLE_LOGD(LOG_TAG, "exchangeMTUCb: status=%d, mtu=%d", error->status, mtu); + + NimBLEClient* pClient = (NimBLEClient*)arg; + if (pClient->getConnHandle() != conn_handle) { + return 0; + } + + if (error->status != 0) { + NIMBLE_LOGE(LOG_TAG, "exchangeMTUCb() rc=%d %s", error->status, NimBLEUtils::returnCodeToString(error->status)); + pClient->m_lastErr = error->status; + } + + return 0; +} // exchangeMTUCb + +/** + * @brief Begin the MTU exchange process with the server. + * @returns true if the request was sent successfully. + */ +bool NimBLEClient::exchangeMTU() { + int rc = ble_gattc_exchange_mtu(m_connHandle, NimBLEClient::exchangeMTUCb, this); + if (rc != 0) { + NIMBLE_LOGE(LOG_TAG, "MTU exchange error; rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc)); + m_lastErr = rc; + return false; + } + + return true; +} // exchangeMTU /** * @brief Handle a received GAP event. * @param [in] event The event structure sent by the NimBLE stack. * @param [in] arg A pointer to the client instance that registered for this callback. */ - /*STATIC*/ -int NimBLEClient::handleGapEvent(struct ble_gap_event *event, void *arg) { - NimBLEClient* pClient = (NimBLEClient*)arg; - int rc; +int NimBLEClient::handleGapEvent(struct ble_gap_event* event, void* arg) { + NimBLEClient* pClient = (NimBLEClient*)arg; + int rc = 0; + NimBLETaskData* pTaskData = pClient->m_pTaskData; // save a copy in case client is deleted - NIMBLE_LOGD(LOG_TAG, "Got Client event %s", NimBLEUtils::gapEventToString(event->type)); - - switch(event->type) { + NIMBLE_LOGD(LOG_TAG, ">> handleGapEvent %s", NimBLEUtils::gapEventToString(event->type)); + switch (event->type) { case BLE_GAP_EVENT_DISCONNECT: { + // workaround for bug in NimBLE stack where disconnect event argument is not passed correctly + pClient = NimBLEDevice::getClientByHandle(event->disconnect.conn.conn_handle); + if (pClient == nullptr) { + return 0; + } + rc = event->disconnect.reason; // If Host reset tell the device now before returning to prevent - // any errors caused by calling host functions before resyncing. - switch(rc) { + // any errors caused by calling host functions before re-syncing. + switch (rc) { case BLE_HS_ECONTROLLER: case BLE_HS_ETIMEOUT_HCI: case BLE_HS_ENOTSYNCED: @@ -1006,113 +951,113 @@ int NimBLEClient::handleGapEvent(struct ble_gap_event *event, void *arg) { NimBLEDevice::onReset(rc); break; default: - // Check that the event is for this client. - if(pClient->m_conn_id != event->disconnect.conn.conn_handle) { - return 0; - } break; } - // Stop the disconnect timer since we are now disconnected. - ble_npl_callout_stop(&pClient->m_dcTimer); + NIMBLE_LOGD(LOG_TAG, "disconnect; reason=%d, %s", rc, NimBLEUtils::returnCodeToString(rc)); - // Remove the device from ignore list so we will scan it again - NimBLEDevice::removeIgnored(pClient->m_peerAddress); + pClient->m_terminateFailCount = 0; + pClient->m_asyncSecureAttempt = 0; - // No longer connected, clear the connection ID. - pClient->m_conn_id = BLE_HS_CONN_HANDLE_NONE; + // Don't call the disconnect callback if we are waiting for a connection to complete and it fails + if (rc != (BLE_HS_ERR_HCI_BASE + BLE_ERR_CONN_ESTABLISHMENT) || pClient->m_config.asyncConnect) { + pClient->m_pClientCallbacks->onDisconnect(pClient, rc); + } - // If we received a connected event but did not get established (no PDU) - // then a disconnect event will be sent but we should not send it to the - // app for processing. Instead we will ensure the task is released - // and report the error. - if(!pClient->m_connEstablished) - break; + pClient->m_connHandle = BLE_HS_CONN_HANDLE_NONE; - NIMBLE_LOGI(LOG_TAG, "disconnect; reason=%d, %s", - rc, NimBLEUtils::returnCodeToString(rc)); + if (pClient->m_config.deleteOnDisconnect) { + // If we are set to self delete on disconnect but we have a task waiting on the connection + // completion we will set the flag to delete on connect fail instead of deleting here + // to prevent segmentation faults or double deleting + if (pTaskData != nullptr && rc == (BLE_HS_ERR_HCI_BASE + BLE_ERR_CONN_ESTABLISHMENT)) { + pClient->m_config.deleteOnConnectFail = true; + break; + } + NimBLEDevice::deleteClient(pClient); + } - pClient->m_connEstablished = false; - pClient->m_pClientCallbacks->onDisconnect(pClient, rc); break; } // BLE_GAP_EVENT_DISCONNECT case BLE_GAP_EVENT_CONNECT: { - // If we aren't waiting for this connection response - // we should drop the connection immediately. - if(pClient->isConnected() || pClient->m_pTaskData == nullptr) { + // If we aren't waiting for this connection response we should drop the connection immediately. + if (pClient->isConnected() || (!pClient->m_config.asyncConnect && pClient->m_pTaskData == nullptr)) { ble_gap_terminate(event->connect.conn_handle, BLE_ERR_REM_USER_CONN_TERM); return 0; } rc = event->connect.status; if (rc == 0) { - NIMBLE_LOGI(LOG_TAG, "Connected event"); + pClient->m_connHandle = event->connect.conn_handle; - pClient->m_conn_id = event->connect.conn_handle; - - rc = ble_gattc_exchange_mtu(pClient->m_conn_id, NULL,NULL); - if(rc != 0) { - NIMBLE_LOGE(LOG_TAG, "MTU exchange error; rc=%d %s", - rc, NimBLEUtils::returnCodeToString(rc)); - break; + if (pClient->m_config.asyncConnect) { + pClient->m_pClientCallbacks->onConnect(pClient); } - // In the case of a multi-connecting device we ignore this device when - // scanning since we are already connected to it - NimBLEDevice::addIgnored(pClient->m_peerAddress); + if (pClient->m_config.exchangeMTU) { + if (!pClient->exchangeMTU()) { + rc = pClient->m_lastErr; // sets the error in the task data + break; + } + + return 0; // return as we may have a task waiting for the MTU before releasing it. + } } else { - pClient->m_conn_id = BLE_HS_CONN_HANDLE_NONE; - break; + pClient->m_connHandle = BLE_HS_CONN_HANDLE_NONE; + + if (pClient->m_config.asyncConnect) { + pClient->m_pClientCallbacks->onConnectFail(pClient, rc); + if (pClient->m_config.deleteOnConnectFail) { + NimBLEDevice::deleteClient(pClient); + } + } } - return 0; + break; } // BLE_GAP_EVENT_CONNECT - case BLE_GAP_EVENT_NOTIFY_RX: { - if(pClient->m_conn_id != event->notify_rx.conn_handle) - return 0; - - // If a notification comes before this flag is set we might - // access a vector while it is being cleared in connect() - if(!pClient->m_connEstablished) { + case BLE_GAP_EVENT_TERM_FAILURE: { + if (pClient->m_connHandle != event->term_failure.conn_handle) { return 0; } - NIMBLE_LOGD(LOG_TAG, "Notify Received for handle: %d", - event->notify_rx.attr_handle); + NIMBLE_LOGE(LOG_TAG, "Connection termination failure; rc=%d - retrying", event->term_failure.status); + if (++pClient->m_terminateFailCount > 2) { + ble_hs_sched_reset(BLE_HS_ECONTROLLER); + } else { + ble_gap_terminate(event->term_failure.conn_handle, BLE_ERR_REM_USER_CONN_TERM); + } + return 0; + } // BLE_GAP_EVENT_TERM_FAILURE - for(auto &it: pClient->m_servicesVector) { + case BLE_GAP_EVENT_NOTIFY_RX: { + if (pClient->m_connHandle != event->notify_rx.conn_handle) return 0; + NIMBLE_LOGD(LOG_TAG, "Notify Received for handle: %d", event->notify_rx.attr_handle); + + for (const auto& svc : pClient->m_svcVec) { // Dont waste cycles searching services without this handle in its range - if(it->getEndHandle() < event->notify_rx.attr_handle) { + if (svc->getEndHandle() < event->notify_rx.attr_handle) { continue; } - auto cVector = &it->m_characteristicVector; - NIMBLE_LOGD(LOG_TAG, "checking service %s for handle: %d", - it->getUUID().toString().c_str(), + NIMBLE_LOGD(LOG_TAG, + "checking service %s for handle: %d", + svc->getUUID().toString().c_str(), event->notify_rx.attr_handle); - auto characteristic = cVector->cbegin(); - for(; characteristic != cVector->cend(); ++characteristic) { - if((*characteristic)->m_handle == event->notify_rx.attr_handle) + for (const auto& chr : svc->m_vChars) { + if (chr->getHandle() == event->notify_rx.attr_handle) { + NIMBLE_LOGD(LOG_TAG, "Got Notification for characteristic %s", chr->toString().c_str()); + + uint32_t data_len = OS_MBUF_PKTLEN(event->notify_rx.om); + chr->m_value.setValue(event->notify_rx.om->om_data, data_len); + + if (chr->m_notifyCallback != nullptr) { + chr->m_notifyCallback(chr, event->notify_rx.om->om_data, data_len, !event->notify_rx.indication); + } break; - } - - if(characteristic != cVector->cend()) { - NIMBLE_LOGD(LOG_TAG, "Got Notification for characteristic %s", - (*characteristic)->toString().c_str()); - - uint32_t data_len = OS_MBUF_PKTLEN(event->notify_rx.om); - (*characteristic)->m_value.setValue(event->notify_rx.om->om_data, data_len); - - if ((*characteristic)->m_notifyCallback != nullptr) { - NIMBLE_LOGD(LOG_TAG, "Invoking callback for notification on characteristic %s", - (*characteristic)->toString().c_str()); - (*characteristic)->m_notifyCallback(*characteristic, event->notify_rx.om->om_data, - data_len, !event->notify_rx.indication); } - break; } } @@ -1121,25 +1066,26 @@ int NimBLEClient::handleGapEvent(struct ble_gap_event *event, void *arg) { case BLE_GAP_EVENT_CONN_UPDATE_REQ: case BLE_GAP_EVENT_L2CAP_UPDATE_REQ: { - if(pClient->m_conn_id != event->conn_update_req.conn_handle){ + if (pClient->m_connHandle != event->conn_update_req.conn_handle) { return 0; } NIMBLE_LOGD(LOG_TAG, "Peer requesting to update connection parameters"); - NIMBLE_LOGD(LOG_TAG, "MinInterval: %d, MaxInterval: %d, Latency: %d, Timeout: %d", + NIMBLE_LOGD(LOG_TAG, + "MinInterval: %d, MaxInterval: %d, Latency: %d, Timeout: %d", event->conn_update_req.peer_params->itvl_min, event->conn_update_req.peer_params->itvl_max, event->conn_update_req.peer_params->latency, event->conn_update_req.peer_params->supervision_timeout); - rc = pClient->m_pClientCallbacks->onConnParamsUpdateRequest(pClient, - event->conn_update_req.peer_params) ? 0 : BLE_ERR_CONN_PARMS; + rc = pClient->m_pClientCallbacks->onConnParamsUpdateRequest(pClient, event->conn_update_req.peer_params) + ? 0 + : BLE_ERR_CONN_PARMS; - - if(!rc && event->type == BLE_GAP_EVENT_CONN_UPDATE_REQ ) { - event->conn_update_req.self_params->itvl_min = pClient->m_pConnParams.itvl_min; - event->conn_update_req.self_params->itvl_max = pClient->m_pConnParams.itvl_max; - event->conn_update_req.self_params->latency = pClient->m_pConnParams.latency; - event->conn_update_req.self_params->supervision_timeout = pClient->m_pConnParams.supervision_timeout; + if (!rc && event->type == BLE_GAP_EVENT_CONN_UPDATE_REQ) { + event->conn_update_req.self_params->itvl_min = pClient->m_connParams.itvl_min; + event->conn_update_req.self_params->itvl_max = pClient->m_connParams.itvl_max; + event->conn_update_req.self_params->latency = pClient->m_connParams.latency; + event->conn_update_req.self_params->supervision_timeout = pClient->m_connParams.supervision_timeout; } NIMBLE_LOGD(LOG_TAG, "%s peer params", (rc == 0) ? "Accepted" : "Rejected"); @@ -1147,10 +1093,10 @@ int NimBLEClient::handleGapEvent(struct ble_gap_event *event, void *arg) { } // BLE_GAP_EVENT_CONN_UPDATE_REQ, BLE_GAP_EVENT_L2CAP_UPDATE_REQ case BLE_GAP_EVENT_CONN_UPDATE: { - if(pClient->m_conn_id != event->conn_update.conn_handle){ + if (pClient->m_connHandle != event->conn_update.conn_handle) { return 0; } - if(event->conn_update.status == 0) { + if (event->conn_update.status == 0) { NIMBLE_LOGI(LOG_TAG, "Connection parameters updated."); } else { NIMBLE_LOGE(LOG_TAG, "Update connection parameters failed."); @@ -1159,17 +1105,15 @@ int NimBLEClient::handleGapEvent(struct ble_gap_event *event, void *arg) { } // BLE_GAP_EVENT_CONN_UPDATE case BLE_GAP_EVENT_ENC_CHANGE: { - if(pClient->m_conn_id != event->enc_change.conn_handle){ + if (pClient->m_connHandle != event->enc_change.conn_handle) { return 0; } - if(event->enc_change.status == 0 || - event->enc_change.status == (BLE_HS_ERR_HCI_BASE + BLE_ERR_PINKEY_MISSING)) - { + if (event->enc_change.status == 0 || + event->enc_change.status == (BLE_HS_ERR_HCI_BASE + BLE_ERR_PINKEY_MISSING)) { NimBLEConnInfo peerInfo; rc = ble_gap_conn_find(event->enc_change.conn_handle, &peerInfo.m_desc); if (rc != 0) { - NIMBLE_LOGE(LOG_TAG, "Connection info not found"); rc = 0; break; } @@ -1177,20 +1121,24 @@ int NimBLEClient::handleGapEvent(struct ble_gap_event *event, void *arg) { if (event->enc_change.status == (BLE_HS_ERR_HCI_BASE + BLE_ERR_PINKEY_MISSING)) { // Key is missing, try deleting. ble_store_util_delete_peer(&peerInfo.m_desc.peer_id_addr); + // Attempt a retry if async secure failed. + if (pClient->m_asyncSecureAttempt == 1) { + pClient->secureConnection(true); + } } else { + pClient->m_asyncSecureAttempt = 0; pClient->m_pClientCallbacks->onAuthenticationComplete(peerInfo); } } rc = event->enc_change.status; break; - } //BLE_GAP_EVENT_ENC_CHANGE + } // BLE_GAP_EVENT_ENC_CHANGE case BLE_GAP_EVENT_IDENTITY_RESOLVED: { NimBLEConnInfo peerInfo; rc = ble_gap_conn_find(event->identity_resolved.conn_handle, &peerInfo.m_desc); if (rc != 0) { - NIMBLE_LOGE(LOG_TAG, "Connection info not found"); rc = 0; break; } @@ -1199,45 +1147,51 @@ int NimBLEClient::handleGapEvent(struct ble_gap_event *event, void *arg) { break; } // BLE_GAP_EVENT_IDENTITY_RESOLVED + case BLE_GAP_EVENT_PHY_UPDATE_COMPLETE: { + NimBLEConnInfo peerInfo; + rc = ble_gap_conn_find(event->phy_updated.conn_handle, &peerInfo.m_desc); + if (rc != 0) { + return BLE_ATT_ERR_INVALID_HANDLE; + } + + pClient->m_pClientCallbacks->onPhyUpdate(pClient, event->phy_updated.tx_phy, event->phy_updated.rx_phy); + return 0; + } // BLE_GAP_EVENT_PHY_UPDATE_COMPLETE + case BLE_GAP_EVENT_MTU: { - if(pClient->m_conn_id != event->mtu.conn_handle){ + if (pClient->m_connHandle != event->mtu.conn_handle) { return 0; } - NIMBLE_LOGI(LOG_TAG, "mtu update event; conn_handle=%d mtu=%d", - event->mtu.conn_handle, - event->mtu.value); + + NIMBLE_LOGI(LOG_TAG, "mtu update: mtu=%d", event->mtu.value); + pClient->m_pClientCallbacks->onMTUChange(pClient, event->mtu.value); rc = 0; break; } // BLE_GAP_EVENT_MTU case BLE_GAP_EVENT_PASSKEY_ACTION: { - struct ble_sm_io pkey = {0,0}; - (void)pkey; //warning: variable 'pkey' set but not used [-Wunused-but-set-variable] - - if(pClient->m_conn_id != event->passkey.conn_handle) + if (pClient->m_connHandle != event->passkey.conn_handle) { return 0; + } NimBLEConnInfo peerInfo; rc = ble_gap_conn_find(event->passkey.conn_handle, &peerInfo.m_desc); if (rc != 0) { - NIMBLE_LOGE(LOG_TAG, "Connection info not found"); rc = 0; break; } if (event->passkey.params.action == BLE_SM_IOACT_NUMCMP) { NIMBLE_LOGD(LOG_TAG, "Passkey on device's display: %" PRIu32, event->passkey.params.numcmp); - pClient->m_pClientCallbacks->onConfirmPIN(peerInfo, event->passkey.params.numcmp); - //TODO: Handle out of band pairing + pClient->m_pClientCallbacks->onConfirmPasskey(peerInfo, event->passkey.params.numcmp); } else if (event->passkey.params.action == BLE_SM_IOACT_OOB) { - static uint8_t tem_oob[16] = {0}; - pkey.action = event->passkey.params.action; - for (int i = 0; i < 16; i++) { - pkey.oob[i] = tem_oob[i]; - } - rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey); - NIMBLE_LOGD(LOG_TAG, "ble_sm_inject_io result: %d", rc); - //////// + NIMBLE_LOGD(LOG_TAG, "OOB request received"); + // TODO: Handle out of band pairing + // struct ble_sm_io pkey; + // pkey.action = BLE_SM_IOACT_OOB; + // pClient->onOobPairingRequest(pkey.oob); + // rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey); + // NIMBLE_LOGD(LOG_TAG, "ble_sm_inject_io result: %d", rc); } else if (event->passkey.params.action == BLE_SM_IOACT_INPUT) { NIMBLE_LOGD(LOG_TAG, "Enter the passkey"); pClient->m_pClientCallbacks->onPassKeyEntry(peerInfo); @@ -1253,96 +1207,104 @@ int NimBLEClient::handleGapEvent(struct ble_gap_event *event, void *arg) { } } // Switch - if(pClient->m_pTaskData != nullptr) { - pClient->m_pTaskData->rc = rc; - if(pClient->m_pTaskData->task) { - xTaskNotifyGive(pClient->m_pTaskData->task); - } - pClient->m_pTaskData = nullptr; + if (pTaskData != nullptr) { + NimBLEUtils::taskRelease(*pTaskData, rc); } + NIMBLE_LOGD(LOG_TAG, "<< handleGapEvent"); return 0; } // handleGapEvent - /** * @brief Are we connected to a server? * @return True if we are connected and false if we are not connected. */ -bool NimBLEClient::isConnected() { - return m_conn_id != BLE_HS_CONN_HANDLE_NONE; +bool NimBLEClient::isConnected() const { + return m_connHandle != BLE_HS_CONN_HANDLE_NONE; } // isConnected - /** * @brief Set the callbacks that will be invoked when events are received. * @param [in] pClientCallbacks A pointer to a class to receive the event callbacks. * @param [in] deleteCallbacks If true this will delete the callback class sent when the client is destructed. */ void NimBLEClient::setClientCallbacks(NimBLEClientCallbacks* pClientCallbacks, bool deleteCallbacks) { - if (pClientCallbacks != nullptr){ - m_pClientCallbacks = pClientCallbacks; + if (pClientCallbacks != nullptr) { + m_pClientCallbacks = pClientCallbacks; + m_config.deleteCallbacks = deleteCallbacks; } else { - m_pClientCallbacks = &defaultCallbacks; + m_pClientCallbacks = &defaultCallbacks; + m_config.deleteCallbacks = false; } - m_deleteCallbacks = deleteCallbacks; } // setClientCallbacks - /** * @brief Return a string representation of this client. * @return A string representation of this client. */ -std::string NimBLEClient::toString() { - std::string res = "peer address: " + m_peerAddress.toString(); - res += "\nServices:\n"; +std::string NimBLEClient::toString() const { + std::string res = "peer address: " + m_peerAddress.toString(); + res += "\nServices:\n"; - for(auto &it: m_servicesVector) { + for (const auto& it : m_svcVec) { res += it->toString() + "\n"; } return res; } // toString +static const char* CB_TAG = "NimBLEClientCallbacks"; /** * @brief Get the last error code reported by the NimBLE host * @return int, the NimBLE error code. */ -int NimBLEClient::getLastError() { +int NimBLEClient::getLastError() const { return m_lastErr; } // getLastError - void NimBLEClientCallbacks::onConnect(NimBLEClient* pClient) { - NIMBLE_LOGD("NimBLEClientCallbacks", "onConnect: default"); -} + NIMBLE_LOGD(CB_TAG, "onConnect: default"); +} // onConnect + +void NimBLEClientCallbacks::onConnectFail(NimBLEClient* pClient, int reason) { + NIMBLE_LOGD(CB_TAG, "onConnectFail: default, reason: %d", reason); +} // onConnectFail void NimBLEClientCallbacks::onDisconnect(NimBLEClient* pClient, int reason) { - NIMBLE_LOGD("NimBLEClientCallbacks", "onDisconnect: default"); -} + NIMBLE_LOGD(CB_TAG, "onDisconnect: default, reason: %d", reason); +} // onDisconnect bool NimBLEClientCallbacks::onConnParamsUpdateRequest(NimBLEClient* pClient, const ble_gap_upd_params* params) { - NIMBLE_LOGD("NimBLEClientCallbacks", "onConnParamsUpdateRequest: default"); + NIMBLE_LOGD(CB_TAG, "onConnParamsUpdateRequest: default"); return true; -} +} // onConnParamsUpdateRequest -void NimBLEClientCallbacks::onPassKeyEntry(const NimBLEConnInfo& connInfo){ - NIMBLE_LOGD("NimBLEClientCallbacks", "onPassKeyEntry: default: 123456"); +void NimBLEClientCallbacks::onPassKeyEntry(NimBLEConnInfo& connInfo) { + NIMBLE_LOGD(CB_TAG, "onPassKeyEntry: default: 123456"); NimBLEDevice::injectPassKey(connInfo, 123456); -} //onPassKeyEntry +} // onPassKeyEntry -void NimBLEClientCallbacks::onAuthenticationComplete(const NimBLEConnInfo& connInfo){ - NIMBLE_LOGD("NimBLEClientCallbacks", "onAuthenticationComplete: default"); -} +void NimBLEClientCallbacks::onAuthenticationComplete(NimBLEConnInfo& connInfo) { + NIMBLE_LOGD(CB_TAG, "onAuthenticationComplete: default"); +} // onAuthenticationComplete -void NimBLEClientCallbacks::onIdentity(const NimBLEConnInfo& connInfo){ - NIMBLE_LOGD("NimBLEClientCallbacks", "onIdentity: default"); +void NimBLEClientCallbacks::onIdentity(NimBLEConnInfo& connInfo) { + NIMBLE_LOGD(CB_TAG, "onIdentity: default"); } // onIdentity -void NimBLEClientCallbacks::onConfirmPIN(const NimBLEConnInfo& connInfo, uint32_t pin){ - NIMBLE_LOGD("NimBLEClientCallbacks", "onConfirmPIN: default: true"); - NimBLEDevice::injectConfirmPIN(connInfo, true); -} +void NimBLEClientCallbacks::onConfirmPasskey(NimBLEConnInfo& connInfo, uint32_t pin) { + NIMBLE_LOGD(CB_TAG, "onConfirmPasskey: default: true"); + NimBLEDevice::injectConfirmPasskey(connInfo, true); +} // onConfirmPasskey -#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL */ +void NimBLEClientCallbacks::onMTUChange(NimBLEClient* pClient, uint16_t mtu) { + NIMBLE_LOGD(CB_TAG, "onMTUChange: default"); +} // onMTUChange + +void NimBLEClientCallbacks::onPhyUpdate(NimBLEClient* pClient, uint8_t txPhy, uint8_t rxPhy) { + NIMBLE_LOGD(CB_TAG, "onPhyUpdate: default, txPhy: %d, rxPhy: %d", txPhy, rxPhy); +} // onPhyUpdate +# + +#endif // CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL diff --git a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEClient.h b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEClient.h index acef487bf..eb205e2ed 100644 --- a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEClient.h +++ b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEClient.h @@ -1,135 +1,171 @@ /* - * NimBLEClient.h + * Copyright 2020-2025 Ryan Powell and + * esp-nimble-cpp, NimBLE-Arduino contributors. * - * Created: on Jan 26 2020 - * Author H2zero + * 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 * - * Originally: - * BLEClient.h + * http://www.apache.org/licenses/LICENSE-2.0 * - * Created on: Mar 22, 2017 - * Author: kolban + * 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 MAIN_NIMBLECLIENT_H_ -#define MAIN_NIMBLECLIENT_H_ +#ifndef NIMBLE_CPP_CLIENT_H_ +#define NIMBLE_CPP_CLIENT_H_ #include "nimconfig.h" -#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL) +#if CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL -#include "NimBLEAddress.h" -#include "NimBLEUUID.h" -#include "NimBLEUtils.h" -#include "NimBLEConnInfo.h" -#include "NimBLEAttValue.h" -#include "NimBLEAdvertisedDevice.h" -#include "NimBLERemoteService.h" +# if defined(CONFIG_NIMBLE_CPP_IDF) +# include "host/ble_gap.h" +# else +# include "nimble/nimble/host/include/host/ble_gap.h" +# endif -#include -#include +# include "NimBLEAddress.h" +# include +# include +# include + +class NimBLEAddress; +class NimBLEUUID; class NimBLERemoteService; class NimBLERemoteCharacteristic; -class NimBLEClientCallbacks; class NimBLEAdvertisedDevice; +class NimBLEAttValue; +class NimBLEClientCallbacks; +class NimBLEConnInfo; +struct NimBLETaskData; /** - * @brief A model of a %BLE client. + * @brief A model of a BLE client. */ class NimBLEClient { -public: - bool connect(NimBLEAdvertisedDevice* device, bool deleteAttributes = true); - bool connect(const NimBLEAddress &address, bool deleteAttributes = true); - bool connect(bool deleteAttributes = true); - int disconnect(uint8_t reason = BLE_ERR_REM_USER_CONN_TERM); - NimBLEAddress getPeerAddress(); - void setPeerAddress(const NimBLEAddress &address); - int getRssi(); - std::vector* getServices(bool refresh = false); + public: +# if CONFIG_BT_NIMBLE_ROLE_OBSERVER + bool connect(const NimBLEAdvertisedDevice* device, + bool deleteAttributes = true, + bool asyncConnect = false, + bool exchangeMTU = true); +# endif + bool connect(const NimBLEAddress& address, bool deleteAttributes = true, bool asyncConnect = false, bool exchangeMTU = true); + bool connect(bool deleteAttributes = true, bool asyncConnect = false, bool exchangeMTU = true); + bool disconnect(uint8_t reason = BLE_ERR_REM_USER_CONN_TERM); + bool cancelConnect() const; + void setSelfDelete(bool deleteOnDisconnect, bool deleteOnConnectFail); + NimBLEAddress getPeerAddress() const; + bool setPeerAddress(const NimBLEAddress& address); + int getRssi() const; + bool isConnected() const; + void setClientCallbacks(NimBLEClientCallbacks* pClientCallbacks, bool deleteCallbacks = true); + std::string toString() const; + uint16_t getConnHandle() const; + uint16_t getMTU() const; + bool exchangeMTU(); + bool secureConnection(bool async = false) const; + void setConnectTimeout(uint32_t timeout); + bool setDataLen(uint16_t txOctets); + bool discoverAttributes(); + NimBLEConnInfo getConnInfo() const; + int getLastError() const; + bool updateConnParams(uint16_t minInterval, uint16_t maxInterval, uint16_t latency, uint16_t timeout); + void setConnectionParams(uint16_t minInterval, + uint16_t maxInterval, + uint16_t latency, + uint16_t timeout, + uint16_t scanInterval = 16, + uint16_t scanWindow = 16); + const std::vector& getServices(bool refresh = false); std::vector::iterator begin(); std::vector::iterator end(); + NimBLERemoteCharacteristic* getCharacteristic(uint16_t handle); NimBLERemoteService* getService(const char* uuid); - NimBLERemoteService* getService(const NimBLEUUID &uuid); + NimBLERemoteService* getService(const NimBLEUUID& uuid); void deleteServices(); - size_t deleteService(const NimBLEUUID &uuid); - NimBLEAttValue getValue(const NimBLEUUID &serviceUUID, const NimBLEUUID &characteristicUUID); - bool setValue(const NimBLEUUID &serviceUUID, const NimBLEUUID &characteristicUUID, - const NimBLEAttValue &value, bool response = false); - NimBLERemoteCharacteristic* getCharacteristic(const uint16_t handle); - bool isConnected(); - void setClientCallbacks(NimBLEClientCallbacks *pClientCallbacks, - bool deleteCallbacks = true); - std::string toString(); - uint16_t getConnId(); - void clearConnection(); - bool setConnection(NimBLEConnInfo &conn_info); - bool setConnection(uint16_t conn_id); - uint16_t getMTU(); - bool secureConnection(); - void setConnectTimeout(uint32_t timeout); - void setConnectionParams(uint16_t minInterval, uint16_t maxInterval, - uint16_t latency, uint16_t timeout, - uint16_t scanInterval=16, uint16_t scanWindow=16); - void updateConnParams(uint16_t minInterval, uint16_t maxInterval, - uint16_t latency, uint16_t timeout); - void setDataLen(uint16_t tx_octets); - bool discoverAttributes(); - NimBLEConnInfo getConnInfo(); - int getLastError(); -#if CONFIG_BT_NIMBLE_EXT_ADV - void setConnectPhy(uint8_t mask); -#endif + size_t deleteService(const NimBLEUUID& uuid); + NimBLEAttValue getValue(const NimBLEUUID& serviceUUID, const NimBLEUUID& characteristicUUID); + bool setValue(const NimBLEUUID& serviceUUID, + const NimBLEUUID& characteristicUUID, + const NimBLEAttValue& value, + bool response = false); -private: - NimBLEClient(const NimBLEAddress &peerAddress); +# if CONFIG_BT_NIMBLE_EXT_ADV + void setConnectPhy(uint8_t phyMask); +# endif + bool updatePhy(uint8_t txPhysMask, uint8_t rxPhysMask, uint16_t phyOptions = 0); + bool getPhy(uint8_t* txPhy, uint8_t* rxPhy); + + struct Config { + uint8_t deleteCallbacks : 1; // Delete the callback object when the client is deleted. + uint8_t deleteOnDisconnect : 1; // Delete the client when disconnected. + uint8_t deleteOnConnectFail : 1; // Delete the client when a connection attempt fails. + uint8_t asyncConnect : 1; // Connect asynchronously. + uint8_t exchangeMTU : 1; // Exchange MTU after connection. + }; + + Config getConfig() const; + void setConfig(Config config); + + private: + NimBLEClient(const NimBLEAddress& peerAddress); ~NimBLEClient(); + NimBLEClient(const NimBLEClient&) = delete; + NimBLEClient& operator=(const NimBLEClient&) = delete; - friend class NimBLEDevice; - friend class NimBLERemoteService; + bool retrieveServices(const NimBLEUUID* uuidFilter = nullptr); + static int handleGapEvent(struct ble_gap_event* event, void* arg); + static int exchangeMTUCb(uint16_t conn_handle, const ble_gatt_error* error, uint16_t mtu, void* arg); + static int serviceDiscoveredCB(uint16_t connHandle, + const struct ble_gatt_error* error, + const struct ble_gatt_svc* service, + void* arg); - static int handleGapEvent(struct ble_gap_event *event, void *arg); - static int serviceDiscoveredCB(uint16_t conn_handle, - const struct ble_gatt_error *error, - const struct ble_gatt_svc *service, - void *arg); - static void dcTimerCb(ble_npl_event *event); - bool retrieveServices(const NimBLEUUID *uuid_filter = nullptr); + NimBLEAddress m_peerAddress; + mutable int m_lastErr; + int32_t m_connectTimeout; + mutable NimBLETaskData* m_pTaskData; + std::vector m_svcVec; + NimBLEClientCallbacks* m_pClientCallbacks; + uint16_t m_connHandle; + uint8_t m_terminateFailCount; + mutable uint8_t m_asyncSecureAttempt; + Config m_config; - NimBLEAddress m_peerAddress; - int m_lastErr; - uint16_t m_conn_id; - bool m_connEstablished; - bool m_deleteCallbacks; - int32_t m_connectTimeout; - NimBLEClientCallbacks* m_pClientCallbacks; - ble_task_data_t* m_pTaskData; - ble_npl_callout m_dcTimer; -#if CONFIG_BT_NIMBLE_EXT_ADV - uint8_t m_phyMask; -#endif - - std::vector m_servicesVector; - -private: - friend class NimBLEClientCallbacks; - ble_gap_conn_params m_pConnParams; +# if CONFIG_BT_NIMBLE_EXT_ADV + uint8_t m_phyMask; +# endif + ble_gap_conn_params m_connParams; + friend class NimBLEDevice; + friend class NimBLEServer; }; // class NimBLEClient - /** * @brief Callbacks associated with a %BLE client. */ class NimBLEClientCallbacks { -public: + public: virtual ~NimBLEClientCallbacks() {}; /** * @brief Called after client connects. - * @param [in] pClient A pointer to the calling client object. + * @param [in] pClient A pointer to the connecting client object. */ virtual void onConnect(NimBLEClient* pClient); + /** + * @brief Called when a connection attempt fails. + * @param [in] pClient A pointer to the connecting client object. + * @param [in] reason Contains the reason code for the connection failure. + */ + virtual void onConnectFail(NimBLEClient* pClient, int reason); + /** * @brief Called when disconnected from the server. * @param [in] pClient A pointer to the calling client object. @@ -149,28 +185,49 @@ public: * @brief Called when server requests a passkey for pairing. * @param [in] connInfo A reference to a NimBLEConnInfo instance containing the peer info. */ - virtual void onPassKeyEntry(const NimBLEConnInfo& connInfo); + virtual void onPassKeyEntry(NimBLEConnInfo& connInfo); /** * @brief Called when the pairing procedure is complete. * @param [in] connInfo A reference to a NimBLEConnInfo instance containing the peer info.\n * This can be used to check the status of the connection encryption/pairing. */ - virtual void onAuthenticationComplete(const NimBLEConnInfo& connInfo); + virtual void onAuthenticationComplete(NimBLEConnInfo& connInfo); /** * @brief Called when using numeric comparision for pairing. * @param [in] connInfo A reference to a NimBLEConnInfo instance containing the peer info. * @param [in] pin The pin to compare with the server. */ - virtual void onConfirmPIN(const NimBLEConnInfo& connInfo, uint32_t pin); + virtual void onConfirmPasskey(NimBLEConnInfo& connInfo, uint32_t pin); /** * @brief Called when the peer identity address is resolved. * @param [in] connInfo A reference to a NimBLEConnInfo instance with information */ - virtual void onIdentity(const NimBLEConnInfo& connInfo); + virtual void onIdentity(NimBLEConnInfo& connInfo); + + /** + * @brief Called when the connection MTU changes. + * @param [in] pClient A pointer to the client that the MTU change is associated with. + * @param [in] MTU The new MTU value. + * about the peer connection parameters. + */ + virtual void onMTUChange(NimBLEClient* pClient, uint16_t MTU); + + /** + * @brief Called when the PHY update procedure is complete. + * @param [in] pClient A pointer to the client whose PHY was updated. + * about the peer connection parameters. + * @param [in] txPhy The transmit PHY. + * @param [in] rxPhy The receive PHY. + * Possible values: + * * BLE_GAP_LE_PHY_1M + * * BLE_GAP_LE_PHY_2M + * * BLE_GAP_LE_PHY_CODED + */ + virtual void onPhyUpdate(NimBLEClient* pClient, uint8_t txPhy, uint8_t rxPhy); }; -#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL */ -#endif /* MAIN_NIMBLECLIENT_H_ */ +#endif // CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL +#endif // NIMBLE_CPP_CLIENT_H_ diff --git a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEConnInfo.h b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEConnInfo.h index 274e6d3b0..cd1e4c3ee 100644 --- a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEConnInfo.h +++ b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEConnInfo.h @@ -1,5 +1,28 @@ -#ifndef NIMBLECONNINFO_H_ -#define NIMBLECONNINFO_H_ +/* + * Copyright 2020-2025 Ryan Powell 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_CONNINFO_H_ +#define NIMBLE_CPP_CONNINFO_H_ + +#if defined(CONFIG_NIMBLE_CPP_IDF) +# include "host/ble_gap.h" +#else +# include "nimble/nimble/host/include/host/ble_gap.h" +#endif #include "NimBLEAddress.h" @@ -7,52 +30,55 @@ * @brief Connection information. */ class NimBLEConnInfo { -friend class NimBLEServer; -friend class NimBLEClient; -friend class NimBLECharacteristic; -friend class NimBLEDescriptor; - - ble_gap_conn_desc m_desc{}; - NimBLEConnInfo(){}; - NimBLEConnInfo(ble_gap_conn_desc desc) { m_desc = desc; } -public: + public: /** @brief Gets the over-the-air address of the connected peer */ - NimBLEAddress getAddress() const { return NimBLEAddress(m_desc.peer_ota_addr); } + NimBLEAddress getAddress() const { return NimBLEAddress(m_desc.peer_ota_addr); } /** @brief Gets the ID address of the connected peer */ - NimBLEAddress getIdAddress() const { return NimBLEAddress(m_desc.peer_id_addr); } + NimBLEAddress getIdAddress() const { return NimBLEAddress(m_desc.peer_id_addr); } /** @brief Gets the connection handle (also known as the connection id) of the connected peer */ - uint16_t getConnHandle() const { return m_desc.conn_handle; } + uint16_t getConnHandle() const { return m_desc.conn_handle; } /** @brief Gets the connection interval for this connection (in 1.25ms units) */ - uint16_t getConnInterval() const { return m_desc.conn_itvl; } + uint16_t getConnInterval() const { return m_desc.conn_itvl; } /** @brief Gets the supervision timeout for this connection (in 10ms units) */ - uint16_t getConnTimeout() const { return m_desc.supervision_timeout; } + uint16_t getConnTimeout() const { return m_desc.supervision_timeout; } /** @brief Gets the allowable latency for this connection (unit = number of intervals) */ - uint16_t getConnLatency() const { return m_desc.conn_latency; } + uint16_t getConnLatency() const { return m_desc.conn_latency; } /** @brief Gets the maximum transmission unit size for this connection (in bytes) */ - uint16_t getMTU() const { return ble_att_mtu(m_desc.conn_handle); } + uint16_t getMTU() const { return ble_att_mtu(m_desc.conn_handle); } /** @brief Check if we are in the master role in this connection */ - bool isMaster() const { return (m_desc.role == BLE_GAP_ROLE_MASTER); } + bool isMaster() const { return (m_desc.role == BLE_GAP_ROLE_MASTER); } /** @brief Check if we are in the slave role in this connection */ - bool isSlave() const { return (m_desc.role == BLE_GAP_ROLE_SLAVE); } + bool isSlave() const { return (m_desc.role == BLE_GAP_ROLE_SLAVE); } /** @brief Check if we are connected to a bonded peer */ - bool isBonded() const { return (m_desc.sec_state.bonded == 1); } + bool isBonded() const { return (m_desc.sec_state.bonded == 1); } /** @brief Check if the connection in encrypted */ - bool isEncrypted() const { return (m_desc.sec_state.encrypted == 1); } + bool isEncrypted() const { return (m_desc.sec_state.encrypted == 1); } /** @brief Check if the the connection has been authenticated */ - bool isAuthenticated() const { return (m_desc.sec_state.authenticated == 1); } + bool isAuthenticated() const { return (m_desc.sec_state.authenticated == 1); } /** @brief Gets the key size used to encrypt the connection */ - uint8_t getSecKeySize() const { return m_desc.sec_state.key_size; } + uint8_t getSecKeySize() const { return m_desc.sec_state.key_size; } + + private: + friend class NimBLEServer; + friend class NimBLEClient; + friend class NimBLECharacteristic; + friend class NimBLEDescriptor; + + ble_gap_conn_desc m_desc{}; + NimBLEConnInfo() {}; + NimBLEConnInfo(ble_gap_conn_desc desc) { m_desc = desc; } }; -#endif + +#endif // NIMBLE_CPP_CONNINFO_H_ diff --git a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEDescriptor.cpp b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEDescriptor.cpp index a457782a8..d85d1d1a1 100644 --- a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEDescriptor.cpp +++ b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEDescriptor.cpp @@ -1,31 +1,40 @@ /* - * NimBLEDescriptor.cpp + * Copyright 2020-2025 Ryan Powell and + * esp-nimble-cpp, NimBLE-Arduino contributors. * - * Created: on March 10, 2020 - * Author H2zero + * 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 * - * Originally: + * http://www.apache.org/licenses/LICENSE-2.0 * - * BLEDescriptor.cpp - * - * Created on: Jun 22, 2017 - * Author: kolban + * 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 "nimconfig.h" -#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) - -#include "NimBLEService.h" #include "NimBLEDescriptor.h" -#include "NimBLELog.h" +#if CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL -#include +# include "NimBLEService.h" +# include "NimBLELog.h" -#define NULL_HANDLE (0xffff) +# include -static const char* LOG_TAG = "NimBLEDescriptor"; +static const char* LOG_TAG = "NimBLEDescriptor"; static NimBLEDescriptorCallbacks defaultCallbacks; +/** + * @brief Construct a descriptor + * @param [in] uuid - UUID (const char*) for the descriptor. + * @param [in] properties - Properties for the descriptor. + * @param [in] max_len - The maximum length in bytes that the descriptor value can hold. (Default: 512 bytes for esp32, 20 for all others). + * @param [in] pCharacteristic - pointer to the characteristic instance this descriptor belongs to. + */ +NimBLEDescriptor::NimBLEDescriptor(const char* uuid, uint16_t properties, uint16_t max_len, NimBLECharacteristic* pCharacteristic) + : NimBLEDescriptor(NimBLEUUID(uuid), properties, max_len, pCharacteristic) {} /** * @brief Construct a descriptor @@ -34,239 +43,64 @@ static NimBLEDescriptorCallbacks defaultCallbacks; * @param [in] max_len - The maximum length in bytes that the descriptor value can hold. (Default: 512 bytes for esp32, 20 for all others). * @param [in] pCharacteristic - pointer to the characteristic instance this descriptor belongs to. */ -NimBLEDescriptor::NimBLEDescriptor(const char* uuid, uint16_t properties, uint16_t max_len, - NimBLECharacteristic* pCharacteristic) -: NimBLEDescriptor(NimBLEUUID(uuid), properties, max_len, pCharacteristic) { -} - - -/** - * @brief Construct a descriptor - * @param [in] uuid - UUID (const char*) for the descriptor. - * @param [in] properties - Properties for the descriptor. - * @param [in] max_len - The maximum length in bytes that the descriptor value can hold. (Default: 512 bytes for esp32, 20 for all others). - * @param [in] pCharacteristic - pointer to the characteristic instance this descriptor belongs to. - */ -NimBLEDescriptor::NimBLEDescriptor(NimBLEUUID uuid, uint16_t properties, uint16_t max_len, - NimBLECharacteristic* pCharacteristic) -: m_value(std::min(CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH , (int)max_len), max_len) { - m_uuid = uuid; - m_handle = NULL_HANDLE; // Handle is initially unknown. - m_pCharacteristic = pCharacteristic; - m_pCallbacks = &defaultCallbacks; // No initial callback. - m_properties = 0; - +NimBLEDescriptor::NimBLEDescriptor(const NimBLEUUID& uuid, uint16_t properties, uint16_t max_len, NimBLECharacteristic* pCharacteristic) + : NimBLELocalValueAttribute{uuid, 0, max_len}, m_pCallbacks{&defaultCallbacks}, m_pCharacteristic{pCharacteristic} { // Check if this is the client configuration descriptor and set to removed if true. if (uuid == NimBLEUUID((uint16_t)0x2902)) { NIMBLE_LOGW(LOG_TAG, "Manually created 2902 descriptor has no functionality; please remove."); - m_removed = 1; - } else { - m_removed = 0; + setRemoved(NIMBLE_ATT_REMOVE_HIDE); } - if (properties & BLE_GATT_CHR_F_READ) { // convert uint16_t properties to uint8_t - m_properties |= BLE_ATT_F_READ; + // convert uint16_t properties to uint8_t for descriptor properties + uint8_t descProperties = 0; + if (properties & NIMBLE_PROPERTY::READ) { + descProperties |= BLE_ATT_F_READ; } - if (properties & (BLE_GATT_CHR_F_WRITE_NO_RSP | BLE_GATT_CHR_F_WRITE)) { - m_properties |= BLE_ATT_F_WRITE; + if (properties & (NIMBLE_PROPERTY::WRITE_NR | NIMBLE_PROPERTY::WRITE)) { + descProperties |= BLE_ATT_F_WRITE; } - if (properties & BLE_GATT_CHR_F_READ_ENC) { - m_properties |= BLE_ATT_F_READ_ENC; + if (properties & NIMBLE_PROPERTY::READ_ENC) { + descProperties |= BLE_ATT_F_READ_ENC; } - if (properties & BLE_GATT_CHR_F_READ_AUTHEN) { - m_properties |= BLE_ATT_F_READ_AUTHEN; + if (properties & NIMBLE_PROPERTY::READ_AUTHEN) { + descProperties |= BLE_ATT_F_READ_AUTHEN; } - if (properties & BLE_GATT_CHR_F_READ_AUTHOR) { - m_properties |= BLE_ATT_F_READ_AUTHOR; + if (properties & NIMBLE_PROPERTY::READ_AUTHOR) { + descProperties |= BLE_ATT_F_READ_AUTHOR; } - if (properties & BLE_GATT_CHR_F_WRITE_ENC) { - m_properties |= BLE_ATT_F_WRITE_ENC; + if (properties & NIMBLE_PROPERTY::WRITE_ENC) { + descProperties |= BLE_ATT_F_WRITE_ENC; } - if (properties & BLE_GATT_CHR_F_WRITE_AUTHEN) { - m_properties |= BLE_ATT_F_WRITE_AUTHEN; + if (properties & NIMBLE_PROPERTY::WRITE_AUTHEN) { + descProperties |= BLE_ATT_F_WRITE_AUTHEN; } - if (properties & BLE_GATT_CHR_F_WRITE_AUTHOR) { - m_properties |= BLE_ATT_F_WRITE_AUTHOR; + if (properties & NIMBLE_PROPERTY::WRITE_AUTHOR) { + descProperties |= BLE_ATT_F_WRITE_AUTHOR; } + setProperties(descProperties); } // NimBLEDescriptor - -/** - * @brief NimBLEDescriptor destructor. - */ -NimBLEDescriptor::~NimBLEDescriptor() { -} // ~NimBLEDescriptor - -/** - * @brief Get the BLE handle for this descriptor. - * @return The handle for this descriptor. - */ -uint16_t NimBLEDescriptor::getHandle() { - return m_handle; -} // getHandle - - -/** - * @brief Get the length of the value of this descriptor. - * @return The length (in bytes) of the value of this descriptor. - */ -size_t NimBLEDescriptor::getLength() { - return m_value.size(); -} // getLength - - -/** - * @brief Get the UUID of the descriptor. - */ -NimBLEUUID NimBLEDescriptor::getUUID() { - return m_uuid; -} // getUUID - - -/** - * @brief Get the value of this descriptor. - * @return The NimBLEAttValue of this descriptor. - */ -NimBLEAttValue NimBLEDescriptor::getValue(time_t *timestamp) { - if (timestamp != nullptr) { - m_value.getValue(timestamp); - } - - return m_value; -} // getValue - - -/** - * @brief Get the value of this descriptor as a string. - * @return A std::string instance containing a copy of the descriptor's value. - */ -std::string NimBLEDescriptor::getStringValue() { - return std::string(m_value); -} - - /** * @brief Get the characteristic this descriptor belongs to. * @return A pointer to the characteristic this descriptor belongs to. */ -NimBLECharacteristic* NimBLEDescriptor::getCharacteristic() { +NimBLECharacteristic* NimBLEDescriptor::getCharacteristic() const { return m_pCharacteristic; } // getCharacteristic - -int NimBLEDescriptor::handleGapEvent(uint16_t conn_handle, uint16_t attr_handle, - struct ble_gatt_access_ctxt *ctxt, void *arg) { - (void)conn_handle; - (void)attr_handle; - - const ble_uuid_t *uuid; - int rc; - NimBLEConnInfo peerInfo{}; - NimBLEDescriptor* pDescriptor = (NimBLEDescriptor*)arg; - - NIMBLE_LOGD(LOG_TAG, "Descriptor %s %s event", pDescriptor->getUUID().toString().c_str(), - ctxt->op == BLE_GATT_ACCESS_OP_READ_DSC ? "Read" : "Write"); - - uuid = ctxt->chr->uuid; - if(ble_uuid_cmp(uuid, &pDescriptor->getUUID().getNative()->u) == 0){ - switch(ctxt->op) { - case BLE_GATT_ACCESS_OP_READ_DSC: { - ble_gap_conn_find(conn_handle, &peerInfo.m_desc); - - // If the packet header is only 8 bytes this is a follow up of a long read - // so we don't want to call the onRead() callback again. - if(ctxt->om->om_pkthdr_len > 8 || - conn_handle == BLE_HS_CONN_HANDLE_NONE || - pDescriptor->m_value.size() <= (ble_att_mtu(peerInfo.getConnHandle()) - 3)) { - pDescriptor->m_pCallbacks->onRead(pDescriptor, peerInfo); - } - - ble_npl_hw_enter_critical(); - rc = os_mbuf_append(ctxt->om, pDescriptor->m_value.data(), pDescriptor->m_value.size()); - ble_npl_hw_exit_critical(0); - return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; - } - - case BLE_GATT_ACCESS_OP_WRITE_DSC: { - ble_gap_conn_find(conn_handle, &peerInfo.m_desc); - - uint16_t att_max_len = pDescriptor->m_value.max_size(); - if (ctxt->om->om_len > att_max_len) { - return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; - } - - uint8_t buf[att_max_len]; - size_t len = ctxt->om->om_len; - memcpy(buf, ctxt->om->om_data,len); - os_mbuf *next; - next = SLIST_NEXT(ctxt->om, om_next); - while(next != NULL){ - if((len + next->om_len) > att_max_len) { - return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; - } - memcpy(&buf[len], next->om_data, next->om_len); - len += next->om_len; - next = SLIST_NEXT(next, om_next); - } - - pDescriptor->setValue(buf, len); - pDescriptor->m_pCallbacks->onWrite(pDescriptor, peerInfo); - return 0; - } - default: - break; - } - } - - return BLE_ATT_ERR_UNLIKELY; -} - /** * @brief Set the callback handlers for this descriptor. * @param [in] pCallbacks An instance of a callback structure used to define any callbacks for the descriptor. */ void NimBLEDescriptor::setCallbacks(NimBLEDescriptorCallbacks* pCallbacks) { - if (pCallbacks != nullptr){ + if (pCallbacks != nullptr) { m_pCallbacks = pCallbacks; } else { m_pCallbacks = &defaultCallbacks; } } // setCallbacks - -/** - * @brief Set the handle of this descriptor. - * Set the handle of this descriptor to be the supplied value. - * @param [in] handle The handle to be associated with this descriptor. - * @return N/A. - */ -void NimBLEDescriptor::setHandle(uint16_t handle) { - NIMBLE_LOGD(LOG_TAG, ">> setHandle(0x%.2x): Setting descriptor handle to be 0x%.2x", handle, handle); - m_handle = handle; - NIMBLE_LOGD(LOG_TAG, "<< setHandle()"); -} // setHandle - - -/** - * @brief Set the value of the descriptor. - * @param [in] data The data to set for the descriptor. - * @param [in] length The length of the data in bytes. - */ -void NimBLEDescriptor::setValue(const uint8_t* data, size_t length) { - m_value.setValue(data, length); -} // setValue - - -/** - * @brief Set the value of the descriptor from a `std::vector`.\n - * @param [in] vec The std::vector reference to set the descriptor value from. - */ -void NimBLEDescriptor::setValue(const std::vector& vec) { - return setValue((uint8_t*)&vec[0], vec.size()); -} // setValue - - /** * @brief Set the characteristic this descriptor belongs to. * @param [in] pChar A pointer to the characteristic this descriptor belongs to. @@ -275,18 +109,25 @@ void NimBLEDescriptor::setCharacteristic(NimBLECharacteristic* pChar) { m_pCharacteristic = pChar; } // setCharacteristic - /** * @brief Return a string representation of the descriptor. * @return A string representation of the descriptor. */ -std::string NimBLEDescriptor::toString() { +std::string NimBLEDescriptor::toString() const { char hex[5]; - snprintf(hex, sizeof(hex), "%04x", m_handle); + snprintf(hex, sizeof(hex), "%04x", getHandle()); std::string res = "UUID: " + m_uuid.toString() + ", handle: 0x" + hex; return res; } // toString +void NimBLEDescriptor::readEvent(NimBLEConnInfo& connInfo) { + m_pCallbacks->onRead(this, connInfo); +} // readEvent + +void NimBLEDescriptor::writeEvent(const uint8_t* val, uint16_t len, NimBLEConnInfo& connInfo) { + setValue(val, len); + m_pCallbacks->onWrite(this, connInfo); +} // writeEvent /** * @brief Callback function to support a read request. @@ -294,19 +135,16 @@ std::string NimBLEDescriptor::toString() { * @param [in] connInfo A reference to a NimBLEConnInfo instance containing the peer info. */ void NimBLEDescriptorCallbacks::onRead(NimBLEDescriptor* pDescriptor, NimBLEConnInfo& connInfo) { - (void)pDescriptor; NIMBLE_LOGD("NimBLEDescriptorCallbacks", "onRead: default"); } // onRead - /** * @brief Callback function to support a write request. * @param [in] pDescriptor The descriptor that is the source of the event. * @param [in] connInfo A reference to a NimBLEConnInfo instance containing the peer info. */ void NimBLEDescriptorCallbacks::onWrite(NimBLEDescriptor* pDescriptor, NimBLEConnInfo& connInfo) { - (void)pDescriptor; NIMBLE_LOGD("NimBLEDescriptorCallbacks", "onWrite: default"); } // onWrite -#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL */ +#endif // CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL diff --git a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEDescriptor.h b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEDescriptor.h index e33334c18..907ad09c8 100644 --- a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEDescriptor.h +++ b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEDescriptor.h @@ -1,105 +1,61 @@ /* - * NimBLEDescriptor.h + * Copyright 2020-2025 Ryan Powell and + * esp-nimble-cpp, NimBLE-Arduino contributors. * - * Created: on March 10, 2020 - * Author H2zero + * 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 * - * Originally: + * http://www.apache.org/licenses/LICENSE-2.0 * - * BLEDescriptor.h - * - * Created on: Jun 22, 2017 - * Author: kolban + * 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 MAIN_NIMBLEDESCRIPTOR_H_ -#define MAIN_NIMBLEDESCRIPTOR_H_ +#ifndef NIMBLE_CPP_DESCRIPTOR_H_ +#define NIMBLE_CPP_DESCRIPTOR_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 "NimBLECharacteristic.h" -#include "NimBLEUUID.h" -#include "NimBLEAttValue.h" -#include "NimBLEConnInfo.h" +# include "NimBLELocalValueAttribute.h" +# include -#include - -class NimBLEService; class NimBLECharacteristic; class NimBLEDescriptorCallbacks; - /** - * @brief A model of a %BLE descriptor. + * @brief A model of a BLE descriptor. */ -class NimBLEDescriptor { -public: - NimBLEDescriptor(const char* uuid, uint16_t properties, - uint16_t max_len, +class NimBLEDescriptor : public NimBLELocalValueAttribute { + public: + NimBLEDescriptor(const char* uuid, uint16_t properties, uint16_t maxLen, NimBLECharacteristic* pCharacteristic = nullptr); + + NimBLEDescriptor(const NimBLEUUID& uuid, + uint16_t properties, + uint16_t maxLen, NimBLECharacteristic* pCharacteristic = nullptr); + ~NimBLEDescriptor() = default; - NimBLEDescriptor(NimBLEUUID uuid, uint16_t properties, - uint16_t max_len, - NimBLECharacteristic* pCharacteristic = nullptr); - - ~NimBLEDescriptor(); - - uint16_t getHandle(); - NimBLEUUID getUUID(); - std::string toString(); + std::string toString() const; void setCallbacks(NimBLEDescriptorCallbacks* pCallbacks); - NimBLECharacteristic* getCharacteristic(); + NimBLECharacteristic* getCharacteristic() const; - size_t getLength(); - NimBLEAttValue getValue(time_t *timestamp = nullptr); - std::string getStringValue(); - - void setValue(const uint8_t* data, size_t size); - void setValue(const std::vector& vec); - - /*********************** Template Functions ************************/ - - /** - * @brief Template to set the characteristic value to val. - * @param [in] s The value to set. - */ - template - void setValue(const T &s) { m_value.setValue(s); } - - /** - * @brief Template to convert the descriptor data to . - * @tparam T The type to convert the data to. - * @param [in] timestamp (Optional) A pointer to a time_t struct to store the time the value was read. - * @param [in] skipSizeCheck (Optional) If true it will skip checking if the data size is less than sizeof(). - * @return The data converted to or NULL if skipSizeCheck is false and the data is less than sizeof(). - * @details Use: getValue(×tamp, skipSizeCheck); - */ - template - T getValue(time_t *timestamp = nullptr, bool skipSizeCheck = false) { - return m_value.getValue(timestamp, skipSizeCheck); - } - -private: + private: friend class NimBLECharacteristic; friend class NimBLEService; - friend class NimBLE2904; - static int handleGapEvent(uint16_t conn_handle, uint16_t attr_handle, - struct ble_gatt_access_ctxt *ctxt, void *arg); - void setHandle(uint16_t handle); - void setCharacteristic(NimBLECharacteristic* pChar); + void setCharacteristic(NimBLECharacteristic* pChar); + void readEvent(NimBLEConnInfo& connInfo) override; + void writeEvent(const uint8_t* val, uint16_t len, NimBLEConnInfo& connInfo) override; - NimBLEUUID m_uuid; - uint16_t m_handle; - NimBLEDescriptorCallbacks* m_pCallbacks; - NimBLECharacteristic* m_pCharacteristic; - uint8_t m_properties; - NimBLEAttValue m_value; - uint8_t m_removed; + NimBLEDescriptorCallbacks* m_pCallbacks{nullptr}; + NimBLECharacteristic* m_pCharacteristic{nullptr}; }; // NimBLEDescriptor - /** * @brief Callbacks that can be associated with a %BLE descriptors to inform of events. * @@ -108,13 +64,13 @@ private: * sub-classed instance of this class and will be notified when such an event happens. */ class NimBLEDescriptorCallbacks { -public: - virtual ~NimBLEDescriptorCallbacks(){} + public: + virtual ~NimBLEDescriptorCallbacks() = default; virtual void onRead(NimBLEDescriptor* pDescriptor, NimBLEConnInfo& connInfo); virtual void onWrite(NimBLEDescriptor* pDescriptor, NimBLEConnInfo& connInfo); }; -#include "NimBLE2904.h" +# include "NimBLE2904.h" -#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL */ -#endif /* MAIN_NIMBLEDESCRIPTOR_H_ */ +#endif // CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL +#endif // NIMBLE_CPP_DESCRIPTOR_H_ diff --git a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEDevice.cpp b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEDevice.cpp index 817f3c3a3..e90a94b4b 100644 --- a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEDevice.cpp +++ b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEDevice.cpp @@ -1,46 +1,48 @@ /* - * NimBLEDevice.cpp + * Copyright 2020-2025 Ryan Powell and + * esp-nimble-cpp, NimBLE-Arduino contributors. * - * Created: on Jan 24 2020 - * Author H2zero + * 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 * - * Originally: + * http://www.apache.org/licenses/LICENSE-2.0 * - * BLEDevice.cpp - * - * Created on: Mar 16, 2017 - * Author: kolban + * 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 "nimconfig.h" -#if defined(CONFIG_BT_ENABLED) - #include "NimBLEDevice.h" -#include "NimBLEUtils.h" +#if CONFIG_BT_ENABLED -#ifdef ESP_PLATFORM +# ifdef ESP_PLATFORM # include "esp_err.h" -# include "esp_bt.h" +# ifndef CONFIG_IDF_TARGET_ESP32P4 +# include "esp_bt.h" +# endif # include "nvs_flash.h" # if defined(CONFIG_NIMBLE_CPP_IDF) -# if (ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0) || CONFIG_BT_NIMBLE_LEGACY_VHCI_ENABLE) -# include "esp_nimble_hci.h" -# endif -# include "nimble/nimble_port.h" -# include "nimble/nimble_port_freertos.h" -# include "host/ble_hs.h" -# include "host/ble_hs_pvcy.h" -# include "host/util/util.h" -# include "services/gap/ble_svc_gap.h" -# include "services/gatt/ble_svc_gatt.h" +# if (ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0) || CONFIG_BT_NIMBLE_LEGACY_VHCI_ENABLE) +# include "esp_nimble_hci.h" +# endif +# include "nimble/nimble_port.h" +# include "nimble/nimble_port_freertos.h" +# include "host/ble_hs.h" +# include "host/ble_hs_pvcy.h" +# include "host/util/util.h" +# include "services/gap/ble_svc_gap.h" +# include "services/gatt/ble_svc_gatt.h" # else -# include "nimble/esp_port/esp-hci/include/esp_nimble_hci.h" +# include "nimble/esp_port/esp-hci/include/esp_nimble_hci.h" # endif -#else +# else # include "nimble/nimble/controller/include/controller/ble_phy.h" -#endif +# endif -#ifndef CONFIG_NIMBLE_CPP_IDF +# ifndef CONFIG_NIMBLE_CPP_IDF # include "nimble/porting/nimble/include/nimble/nimble_port.h" # include "nimble/porting/npl/freertos/include/nimble/nimble_port_freertos.h" # include "nimble/nimble/host/include/host/ble_hs.h" @@ -48,58 +50,81 @@ # include "nimble/nimble/host/util/include/host/util/util.h" # include "nimble/nimble/host/services/gap/include/services/gap/ble_svc_gap.h" # include "nimble/nimble/host/services/gatt/include/services/gatt/ble_svc_gatt.h" -#endif +# endif -#if defined(ESP_PLATFORM) && defined(CONFIG_ENABLE_ARDUINO_DEPENDS) +# if defined(ESP_PLATFORM) && defined(CONFIG_ENABLE_ARDUINO_DEPENDS) # include "esp32-hal-bt.h" -#endif +# endif -#include "NimBLELog.h" +# include "NimBLELog.h" static const char* LOG_TAG = "NimBLEDevice"; +extern "C" void ble_store_config_init(void); + /** * Singletons for the NimBLEDevice. */ -static bool initialized = false; -#if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER) -NimBLEScan* NimBLEDevice::m_pScan = nullptr; -#endif -#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) -NimBLEServer* NimBLEDevice::m_pServer = nullptr; -#endif -uint32_t NimBLEDevice::m_passkey = 123456; -bool NimBLEDevice::m_synced = false; -#if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER) +NimBLEDeviceCallbacks NimBLEDevice::defaultDeviceCallbacks{}; +NimBLEDeviceCallbacks* NimBLEDevice::m_pDeviceCallbacks = &defaultDeviceCallbacks; + +# if CONFIG_BT_NIMBLE_ROLE_OBSERVER +NimBLEScan* NimBLEDevice::m_pScan = nullptr; +# endif + +# if CONFIG_BT_NIMBLE_ROLE_PERIPHERAL +NimBLEServer* NimBLEDevice::m_pServer = nullptr; +# if CONFIG_BT_NIMBLE_L2CAP_COC_MAX_NUM > 0 +NimBLEL2CAPServer* NimBLEDevice::m_pL2CAPServer = nullptr; +# endif +# endif + +# if CONFIG_BT_NIMBLE_ROLE_BROADCASTER # if CONFIG_BT_NIMBLE_EXT_ADV NimBLEExtAdvertising* NimBLEDevice::m_bleAdvertising = nullptr; # else NimBLEAdvertising* NimBLEDevice::m_bleAdvertising = nullptr; # endif -#endif +# endif -gap_event_handler NimBLEDevice::m_customGapHandler = nullptr; -ble_gap_event_listener NimBLEDevice::m_listener; -#if defined( CONFIG_BT_NIMBLE_ROLE_CENTRAL) -std::list NimBLEDevice::m_cList; -#endif -std::list NimBLEDevice::m_ignoreList; -std::vector NimBLEDevice::m_whiteList; -uint8_t NimBLEDevice::m_own_addr_type = BLE_OWN_ADDR_PUBLIC; -#ifdef ESP_PLATFORM -# ifdef CONFIG_BTDM_BLE_SCAN_DUPL -uint16_t NimBLEDevice::m_scanDuplicateSize = CONFIG_BTDM_SCAN_DUPL_CACHE_SIZE; -uint8_t NimBLEDevice::m_scanFilterMode = CONFIG_BTDM_SCAN_DUPL_TYPE; +# if CONFIG_BT_NIMBLE_ROLE_CENTRAL +std::array NimBLEDevice::m_pClients{}; +# endif + +bool NimBLEDevice::m_initialized{false}; +uint32_t NimBLEDevice::m_passkey{123456}; +bool NimBLEDevice::m_synced{false}; +ble_gap_event_listener NimBLEDevice::m_listener{}; +std::vector NimBLEDevice::m_whiteList{}; +uint8_t NimBLEDevice::m_ownAddrType{BLE_OWN_ADDR_PUBLIC}; + +# ifdef ESP_PLATFORM +# if CONFIG_BTDM_BLE_SCAN_DUPL +uint16_t NimBLEDevice::m_scanDuplicateSize{CONFIG_BTDM_SCAN_DUPL_CACHE_SIZE}; +uint8_t NimBLEDevice::m_scanFilterMode{CONFIG_BTDM_SCAN_DUPL_TYPE}; +uint16_t NimBLEDevice::m_scanDuplicateResetTime{0}; +# elif CONFIG_BT_LE_SCAN_DUPL +uint16_t NimBLEDevice::m_scanDuplicateSize{CONFIG_BT_LE_LL_DUP_SCAN_LIST_COUNT}; +uint8_t NimBLEDevice::m_scanFilterMode{CONFIG_BT_LE_SCAN_DUPL_TYPE}; +uint16_t NimBLEDevice::m_scanDuplicateResetTime{0}; +extern "C" int ble_vhci_disc_duplicate_set_max_cache_size(int max_cache_size); +extern "C" int ble_vhci_disc_duplicate_set_period_refresh_time(int refresh_period_time); +extern "C" int ble_vhci_disc_duplicate_mode_disable(int mode); +extern "C" int ble_vhci_disc_duplicate_mode_enable(int mode); # endif -#endif +# endif +/* -------------------------------------------------------------------------- */ +/* SERVER FUNCTIONS */ +/* -------------------------------------------------------------------------- */ + +# if CONFIG_BT_NIMBLE_ROLE_PERIPHERAL /** - * @brief Create a new instance of a server. - * @return A new instance of the server. + * @brief Create an instance of a server. + * @return A pointer to the instance of the server. */ -#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) -/* STATIC */ NimBLEServer* NimBLEDevice::createServer() { - if(NimBLEDevice::m_pServer == nullptr) { +NimBLEServer* NimBLEDevice::createServer() { + if (NimBLEDevice::m_pServer == nullptr) { NimBLEDevice::m_pServer = new NimBLEServer(); ble_gatts_reset(); ble_svc_gap_init(); @@ -109,54 +134,73 @@ uint8_t NimBLEDevice::m_scanFilterMode = CONFIG_BTDM_SCAN_DU return m_pServer; } // createServer - /** * @brief Get the instance of the server. - * @return A pointer to the server instance. + * @return A pointer to the server instance or nullptr if none have been created. */ -/* STATIC */ NimBLEServer* NimBLEDevice::getServer() { +NimBLEServer* NimBLEDevice::getServer() { return m_pServer; } // getServer -#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) +# if CONFIG_BT_NIMBLE_L2CAP_COC_MAX_NUM +/** + * @brief Create an instance of a L2CAP server. + * @return A pointer to the instance of the L2CAP server. + */ +NimBLEL2CAPServer* NimBLEDevice::createL2CAPServer() { + if (NimBLEDevice::m_pL2CAPServer == nullptr) { + NimBLEDevice::m_pL2CAPServer = new NimBLEL2CAPServer(); + } + return m_pL2CAPServer; +} // createL2CAPServer -#if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER) +/** + * @brief Get the instance of the L2CAP server. + * @return A pointer to the L2CAP server instance or nullptr if none have been created. + */ +NimBLEL2CAPServer* NimBLEDevice::getL2CAPServer() { + return m_pL2CAPServer; +} // getL2CAPServer +# endif // #if CONFIG_BT_NIMBLE_L2CAP_COC_MAX_NUM +# endif // #if CONFIG_BT_NIMBLE_ROLE_PERIPHERAL + +/* -------------------------------------------------------------------------- */ +/* ADVERTISING FUNCTIONS */ +/* -------------------------------------------------------------------------- */ + +# if CONFIG_BT_NIMBLE_ROLE_BROADCASTER # if CONFIG_BT_NIMBLE_EXT_ADV /** - * @brief Get the instance of the advertising object. - * @return A pointer to the advertising object. + * @brief Get the instance of the extended advertising object. + * @return A pointer to the extended advertising object. */ NimBLEExtAdvertising* NimBLEDevice::getAdvertising() { - if(m_bleAdvertising == nullptr) { + if (m_bleAdvertising == nullptr) { m_bleAdvertising = new NimBLEExtAdvertising(); } + return m_bleAdvertising; } - /** * @brief Convenience function to begin advertising. - * @param [in] inst_id The extended advertisement instance ID to start. + * @param [in] instId The extended advertisement instance ID to start. * @param [in] duration How long to advertise for in milliseconds, 0 = forever (default). - * @param [in] max_events Maximum number of advertisement events to send, 0 = no limit (default). + * @param [in] maxEvents Maximum number of advertisement events to send, 0 = no limit (default). * @return True if advertising started successfully. */ -bool NimBLEDevice::startAdvertising(uint8_t inst_id, - int duration, - int max_events) { - return getAdvertising()->start(inst_id, duration, max_events); +bool NimBLEDevice::startAdvertising(uint8_t instId, int duration, int maxEvents) { + return getAdvertising()->start(instId, duration, maxEvents); } // startAdvertising - /** * @brief Convenience function to stop advertising a data set. - * @param [in] inst_id The extended advertisement instance ID to stop advertising. + * @param [in] instId The extended advertisement instance ID to stop advertising. * @return True if advertising stopped successfully. */ -bool NimBLEDevice::stopAdvertising(uint8_t inst_id) { - return getAdvertising()->stop(inst_id); +bool NimBLEDevice::stopAdvertising(uint8_t instId) { + return getAdvertising()->stop(instId); } // stopAdvertising - # endif # if !CONFIG_BT_NIMBLE_EXT_ADV || defined(_DOXYGEN_) @@ -165,13 +209,12 @@ bool NimBLEDevice::stopAdvertising(uint8_t inst_id) { * @return A pointer to the advertising object. */ NimBLEAdvertising* NimBLEDevice::getAdvertising() { - if(m_bleAdvertising == nullptr) { + if (m_bleAdvertising == nullptr) { m_bleAdvertising = new NimBLEAdvertising(); } return m_bleAdvertising; } - /** * @brief Convenience function to begin advertising. * @param [in] duration The duration in milliseconds to advertise for, default = forever. @@ -189,337 +232,50 @@ bool NimBLEDevice::startAdvertising(uint32_t duration) { bool NimBLEDevice::stopAdvertising() { return getAdvertising()->stop(); } // stopAdvertising -#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER) +# endif // #if CONFIG_BT_NIMBLE_ROLE_BROADCASTER +/* -------------------------------------------------------------------------- */ +/* SCAN FUNCTIONS */ +/* -------------------------------------------------------------------------- */ /** * @brief Retrieve the Scan object that we use for scanning. * @return The scanning object reference. This is a singleton object. The caller should not * try and release/delete it. */ -#if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER) -/* STATIC */ +# if CONFIG_BT_NIMBLE_ROLE_OBSERVER NimBLEScan* NimBLEDevice::getScan() { if (m_pScan == nullptr) { m_pScan = new NimBLEScan(); } + return m_pScan; } // getScan -#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER) - -/** - * @brief Creates a new client object and maintains a list of all client objects - * each client can connect to 1 peripheral device. - * @param [in] peerAddress An optional peer address that is copied to the new client - * object, allows for calling NimBLEClient::connect(bool) without a device or address parameter. - * @return A reference to the new client object. - */ -#if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL) -/* STATIC */ -NimBLEClient* NimBLEDevice::createClient(NimBLEAddress peerAddress) { - if(m_cList.size() >= NIMBLE_MAX_CONNECTIONS) { - NIMBLE_LOGW(LOG_TAG,"Number of clients exceeds Max connections. Cur=%d Max=%d", - m_cList.size(), NIMBLE_MAX_CONNECTIONS); - } - - NimBLEClient* pClient = new NimBLEClient(peerAddress); - m_cList.push_back(pClient); - - return pClient; -} // createClient - - -/** - * @brief Delete the client object and remove it from the list.\n - * Checks if it is connected or trying to connect and disconnects/stops it first. - * @param [in] pClient A pointer to the client object. - */ -/* STATIC */ -bool NimBLEDevice::deleteClient(NimBLEClient* pClient) { - if(pClient == nullptr) { - return false; - } - - // Set the connection established flag to false to stop notifications - // from accessing the attribute vectors while they are being deleted. - pClient->m_connEstablished = false; - int rc =0; - - if(pClient->isConnected()) { - rc = pClient->disconnect(); - if (rc != 0 && rc != BLE_HS_EALREADY && rc != BLE_HS_ENOTCONN) { - return false; - } - - while(pClient->isConnected()) { - taskYIELD(); - } - // Since we set the flag to false the app will not get a callback - // in the disconnect event so we call it here for good measure. - pClient->m_pClientCallbacks->onDisconnect(pClient, BLE_ERR_CONN_TERM_LOCAL); - - } else if(pClient->m_pTaskData != nullptr) { - rc = ble_gap_conn_cancel(); - if (rc != 0 && rc != BLE_HS_EALREADY) { - return false; - } - while(pClient->m_pTaskData != nullptr) { - taskYIELD(); - } - } - - m_cList.remove(pClient); - delete pClient; - - return true; -} // deleteClient - - -/** - * @brief Get the list of created client objects. - * @return A pointer to the list of clients. - */ -/* STATIC */ -std::list* NimBLEDevice::getClientList() { - return &m_cList; -} // getClientList - - -/** - * @brief Get the number of created client objects. - * @return Number of client objects created. - */ -/* STATIC */ -size_t NimBLEDevice::getClientListSize() { - return m_cList.size(); -} // getClientList - - -/** - * @brief Get a reference to a client by connection ID. - * @param [in] conn_id The client connection ID to search for. - * @return A pointer to the client object with the specified connection ID or nullptr. - */ -/* STATIC */ -NimBLEClient* NimBLEDevice::getClientByID(uint16_t conn_id) { - for(auto it = m_cList.cbegin(); it != m_cList.cend(); ++it) { - if((*it)->getConnId() == conn_id) { - return (*it); - } - } - - return nullptr; -} // getClientByID - - -/** - * @brief Get a reference to a client by peer address. - * @param [in] peer_addr The address of the peer to search for. - * @return A pointer to the client object with the peer address. - */ -/* STATIC */ -NimBLEClient* NimBLEDevice::getClientByPeerAddress(const NimBLEAddress &peer_addr) { - for(auto it = m_cList.cbegin(); it != m_cList.cend(); ++it) { - if((*it)->getPeerAddress().equals(peer_addr)) { - return (*it); - } - } - return nullptr; -} // getClientPeerAddress - - -/** - * @brief Finds the first disconnected client in the list. - * @return A pointer to the first client object that is not connected to a peer. - */ -/* STATIC */ -NimBLEClient* NimBLEDevice::getDisconnectedClient() { - for(auto it = m_cList.cbegin(); it != m_cList.cend(); ++it) { - if(!(*it)->isConnected()) { - return (*it); - } - } - return nullptr; -} // getDisconnectedClient - -#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL) - -#ifdef ESP_PLATFORM -/** - * @brief Set the transmission power. - * @param [in] powerLevel The power level to set, can be one of: - * * ESP_PWR_LVL_N12 = 0, Corresponding to -12dbm - * * ESP_PWR_LVL_N9 = 1, Corresponding to -9dbm - * * ESP_PWR_LVL_N6 = 2, Corresponding to -6dbm - * * ESP_PWR_LVL_N3 = 3, Corresponding to -3dbm - * * ESP_PWR_LVL_N0 = 4, Corresponding to 0dbm - * * ESP_PWR_LVL_P3 = 5, Corresponding to +3dbm - * * ESP_PWR_LVL_P6 = 6, Corresponding to +6dbm - * * ESP_PWR_LVL_P9 = 7, Corresponding to +9dbm - * @param [in] powerType The BLE function to set the power level for, can be one of: - * * ESP_BLE_PWR_TYPE_CONN_HDL0 = 0, For connection handle 0 - * * ESP_BLE_PWR_TYPE_CONN_HDL1 = 1, For connection handle 1 - * * ESP_BLE_PWR_TYPE_CONN_HDL2 = 2, For connection handle 2 - * * ESP_BLE_PWR_TYPE_CONN_HDL3 = 3, For connection handle 3 - * * ESP_BLE_PWR_TYPE_CONN_HDL4 = 4, For connection handle 4 - * * ESP_BLE_PWR_TYPE_CONN_HDL5 = 5, For connection handle 5 - * * ESP_BLE_PWR_TYPE_CONN_HDL6 = 6, For connection handle 6 - * * ESP_BLE_PWR_TYPE_CONN_HDL7 = 7, For connection handle 7 - * * ESP_BLE_PWR_TYPE_CONN_HDL8 = 8, For connection handle 8 - * * ESP_BLE_PWR_TYPE_ADV = 9, For advertising - * * ESP_BLE_PWR_TYPE_SCAN = 10, For scan - * * ESP_BLE_PWR_TYPE_DEFAULT = 11, For default, if not set other, it will use default value - */ -/* STATIC */ -void NimBLEDevice::setPower(esp_power_level_t powerLevel, esp_ble_power_type_t powerType) { - NIMBLE_LOGD(LOG_TAG, ">> setPower: %d (type: %d)", powerLevel, powerType); - - esp_err_t errRc = esp_ble_tx_power_set(powerType, powerLevel); - if (errRc != ESP_OK) { - NIMBLE_LOGE(LOG_TAG, "esp_ble_tx_power_set: rc=%d", errRc); - } - - NIMBLE_LOGD(LOG_TAG, "<< setPower"); -} // setPower - - -/** - * @brief Get the transmission power. - * @param [in] powerType The power level to set, can be one of: - * * ESP_BLE_PWR_TYPE_CONN_HDL0 = 0, For connection handle 0 - * * ESP_BLE_PWR_TYPE_CONN_HDL1 = 1, For connection handle 1 - * * ESP_BLE_PWR_TYPE_CONN_HDL2 = 2, For connection handle 2 - * * ESP_BLE_PWR_TYPE_CONN_HDL3 = 3, For connection handle 3 - * * ESP_BLE_PWR_TYPE_CONN_HDL4 = 4, For connection handle 4 - * * ESP_BLE_PWR_TYPE_CONN_HDL5 = 5, For connection handle 5 - * * ESP_BLE_PWR_TYPE_CONN_HDL6 = 6, For connection handle 6 - * * ESP_BLE_PWR_TYPE_CONN_HDL7 = 7, For connection handle 7 - * * ESP_BLE_PWR_TYPE_CONN_HDL8 = 8, For connection handle 8 - * * ESP_BLE_PWR_TYPE_ADV = 9, For advertising - * * ESP_BLE_PWR_TYPE_SCAN = 10, For scan - * * ESP_BLE_PWR_TYPE_DEFAULT = 11, For default, if not set other, it will use default value - * @return the power level currently used by the type specified. - */ -/* STATIC */ -int NimBLEDevice::getPower(esp_ble_power_type_t powerType) { - switch(esp_ble_tx_power_get(powerType)) { - case ESP_PWR_LVL_N12: - return -12; - case ESP_PWR_LVL_N9: - return -9; - case ESP_PWR_LVL_N6: - return -6; - case ESP_PWR_LVL_N3: - return -3; - case ESP_PWR_LVL_N0: - return 0; - case ESP_PWR_LVL_P3: - return 3; - case ESP_PWR_LVL_P6: - return 6; - case ESP_PWR_LVL_P9: - return 9; - default: - return BLE_HS_ADV_TX_PWR_LVL_AUTO; - } -} // getPower - -#else - -void NimBLEDevice::setPower(int dbm) { - ble_phy_txpwr_set(dbm); -} - - -int NimBLEDevice::getPower() { - return ble_phy_txpwr_get(); -} -#endif - -/** - * @brief Get our device address. - * @return A NimBLEAddress object of our public address if we have one, - * if not then our current random address. - */ -/* STATIC*/ -NimBLEAddress NimBLEDevice::getAddress() { - ble_addr_t addr = {m_own_addr_type, 0}; - - if(BLE_HS_ENOADDR == ble_hs_id_copy_addr(m_own_addr_type, addr.val, NULL)) { - // NIMBLE_LOGD(LOG_TAG, "Public address not found, checking random"); - // addr.type = BLE_ADDR_RANDOM; - // ble_hs_id_copy_addr(BLE_ADDR_RANDOM, addr.val, NULL); - return NimBLEAddress(); // return blank to report error - } - - return NimBLEAddress(addr); -} // getAddress - - -/** - * @brief Return a string representation of the address of this device. - * @return A string representation of this device address. - */ -/* STATIC */ -std::string NimBLEDevice::toString() { - return getAddress().toString(); -} // toString - - -/** - * @brief Setup local mtu that will be used to negotiate mtu during request from client peer. - * @param [in] mtu Value to set local mtu: - * * This should be larger than 23 and lower or equal to BLE_ATT_MTU_MAX = 527. - */ -/* STATIC */ -int NimBLEDevice::setMTU(uint16_t mtu) { - NIMBLE_LOGD(LOG_TAG, ">> setLocalMTU: %d", mtu); - - int rc = ble_att_set_preferred_mtu(mtu); - - if (rc != 0) { - NIMBLE_LOGE(LOG_TAG, "Could not set local mtu value to: %d", mtu); - } - - NIMBLE_LOGD(LOG_TAG, "<< setLocalMTU"); - return rc; -} // setMTU - - -/** - * @brief Get local MTU value set. - * @return The current preferred MTU setting. - */ -/* STATIC */ -uint16_t NimBLEDevice::getMTU() { - return ble_att_preferred_mtu(); -} - - -#ifdef ESP_PLATFORM -# ifdef CONFIG_BTDM_BLE_SCAN_DUPL +# ifdef ESP_PLATFORM +# if CONFIG_BTDM_BLE_SCAN_DUPL || CONFIG_BT_LE_SCAN_DUPL /** * @brief Set the duplicate filter cache size for filtering scanned devices. - * @param [in] cacheSize The number of advertisements filtered before the cache is reset.\n + * @param [in] size The number of advertisements filtered before the cache is reset.\n * Range is 10-1000, a larger value will reduce how often the same devices are reported. * @details Must only be called before calling NimBLEDevice::init. */ -/*STATIC*/ -void NimBLEDevice::setScanDuplicateCacheSize(uint16_t cacheSize) { - if(initialized) { +void NimBLEDevice::setScanDuplicateCacheSize(uint16_t size) { + if (m_initialized) { NIMBLE_LOGE(LOG_TAG, "Cannot change scan cache size while initialized"); return; - } else if(cacheSize > 1000 || cacheSize <10) { - NIMBLE_LOGE(LOG_TAG, "Invalid scan cache size; min=10 max=1000"); - return; + } else { + if (size > 1000) { + size = 1000; + } else if (size < 10) { + size = 10; + } } - m_scanDuplicateSize = cacheSize; + NIMBLE_LOGD(LOG_TAG, "Set duplicate cache size to: %u", size); + m_scanDuplicateSize = size; } - - /** * @brief Set the duplicate filter mode for filtering scanned devices. * @param [in] mode One of three possible options: @@ -533,44 +289,349 @@ void NimBLEDevice::setScanDuplicateCacheSize(uint16_t cacheSize) { except if the data in the advertisement has changed, then it will be reported again. * @details Must only be called before calling NimBLEDevice::init. */ -/*STATIC*/ void NimBLEDevice::setScanFilterMode(uint8_t mode) { - if(initialized) { + if (m_initialized) { NIMBLE_LOGE(LOG_TAG, "Cannot change scan duplicate type while initialized"); return; - } else if(mode > 2) { + } else if (mode > 2) { NIMBLE_LOGE(LOG_TAG, "Invalid scan duplicate type"); return; } m_scanFilterMode = mode; } -# endif // CONFIG_BTDM_BLE_SCAN_DUPL -#endif // ESP_PLATFORM -#if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL) || defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) +/** + * @brief Set the time in seconds to reset the duplicate cache. + * @param [in] time The time in seconds to reset the cache. + * @details When the cache is reset all scanned devices will be reported again + * even if already seen in the current scan. If set to 0 the cache will never be reset. + */ +void NimBLEDevice::setScanDuplicateCacheResetTime(uint16_t time) { + if (m_initialized) { + NIMBLE_LOGE(LOG_TAG, "Cannot change scan cache reset time while initialized"); + return; + } else if (time > 1000) { + NIMBLE_LOGE(LOG_TAG, "Invalid scan cache reset time"); + return; + } + + NIMBLE_LOGD(LOG_TAG, "Set duplicate cache reset time to: %u", time); + m_scanDuplicateResetTime = time; +} +# endif // CONFIG_BTDM_BLE_SCAN_DUPL || CONFIG_BT_LE_SCAN_DUPL +# endif // ESP_PLATFORM +# endif // CONFIG_BT_NIMBLE_ROLE_OBSERVER + +/* -------------------------------------------------------------------------- */ +/* CLIENT FUNCTIONS */ +/* -------------------------------------------------------------------------- */ + +# if CONFIG_BT_NIMBLE_ROLE_CENTRAL +/** + * @brief Creates a new client object, each client can connect to 1 peripheral device. + * @return A pointer to the new client object, or nullptr on error. + */ +NimBLEClient* NimBLEDevice::createClient() { + return createClient(NimBLEAddress{}); +} // createClient + +/** + * @brief Creates a new client object, each client can connect to 1 peripheral device. + * @param [in] peerAddress A peer address reference that is copied to the new client + * object, allows for calling NimBLEClient::connect(bool) without a device or address parameter. + * @return A pointer to the new client object, or nullptr on error. + */ +NimBLEClient* NimBLEDevice::createClient(const NimBLEAddress& peerAddress) { + for (auto& clt : m_pClients) { + if (clt == nullptr) { + clt = new NimBLEClient(peerAddress); + return clt; + } + } + + NIMBLE_LOGE(LOG_TAG, "Unable to create client; already at max: %d", NIMBLE_MAX_CONNECTIONS); + return nullptr; +} // createClient + +/** + * @brief Delete the client object and remove it from the list.\n + * Checks if it is connected or trying to connect and disconnects/stops it first. + * @param [in] pClient A pointer to the client object. + */ +bool NimBLEDevice::deleteClient(NimBLEClient* pClient) { + if (pClient == nullptr) { + return false; + } + + for (auto& clt : m_pClients) { + if (clt == pClient) { + if (clt->isConnected()) { + clt->m_config.deleteOnDisconnect = true; + if (!clt->disconnect()) { + break; + } + } else if (pClient->m_pTaskData != nullptr) { + clt->m_config.deleteOnConnectFail = true; + if (!clt->cancelConnect()) { + break; + } + } else { + delete clt; + clt = nullptr; + } + + return true; + } + } + + return false; +} // deleteClient + +/** + * @brief Get the number of created client objects. + * @return Number of client objects created. + */ +size_t NimBLEDevice::getCreatedClientCount() { + size_t count = 0; + for (const auto clt : m_pClients) { + if (clt != nullptr) { + count++; + } + } + + return count; +} // getCreatedClientCount + +/** + * @brief Get a reference to a client by connection handle. + * @param [in] connHandle The client connection handle to search for. + * @return A pointer to the client object with the specified connection handle or nullptr. + */ +NimBLEClient* NimBLEDevice::getClientByHandle(uint16_t connHandle) { + for (const auto clt : m_pClients) { + if (clt != nullptr && clt->getConnHandle() == connHandle) { + return clt; + } + } + + return nullptr; +} // getClientByHandle + +/** + * @brief Get a reference to a client by peer address. + * @param [in] addr The address of the peer to search for. + * @return A pointer to the client object with the peer address or nullptr. + */ +NimBLEClient* NimBLEDevice::getClientByPeerAddress(const NimBLEAddress& addr) { + for (const auto clt : m_pClients) { + if (clt != nullptr && clt->getPeerAddress() == addr) { + return clt; + } + } + + return nullptr; +} // getClientPeerAddress + +/** + * @brief Finds the first disconnected client available. + * @return A pointer to the first client object that is not connected to a peer or nullptr. + */ +NimBLEClient* NimBLEDevice::getDisconnectedClient() { + for (const auto clt : m_pClients) { + if (clt != nullptr && !clt->isConnected()) { + return clt; + } + } + + return nullptr; +} // getDisconnectedClient + +/** + * @brief Get a list of connected clients. + * @return A vector of connected client objects. + */ +std::vector NimBLEDevice::getConnectedClients() { + std::vector clients; + for (const auto clt : m_pClients) { + if (clt != nullptr && clt->isConnected()) { + clients.push_back(clt); + } + } + + return clients; +} // getConnectedClients + +# endif // CONFIG_BT_NIMBLE_ROLE_CENTRAL + +/* -------------------------------------------------------------------------- */ +/* TRANSMIT POWER */ +/* -------------------------------------------------------------------------- */ + +# ifdef ESP_PLATFORM +# ifndef CONFIG_IDF_TARGET_ESP32P4 +/** + * @brief Get the transmission power. + * @return The power level currently used in esp_power_level_t or a negative value on error. + */ +esp_power_level_t NimBLEDevice::getPowerLevel(esp_ble_power_type_t powerType) { + esp_power_level_t pwr = esp_ble_tx_power_get(powerType); + +# if defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S3) + // workaround for bug when "enhanced tx power" was added + if (pwr == 0xFF) { + pwr = esp_ble_tx_power_get(ESP_BLE_PWR_TYPE_CONN_HDL3); + } +# endif + + return pwr; +} // getPowerLevel + +/** + * @brief Set the transmission power. + * @param [in] powerLevel The power level to set in esp_power_level_t. + * @return True if the power level was set successfully. + */ +bool NimBLEDevice::setPowerLevel(esp_power_level_t powerLevel, esp_ble_power_type_t powerType) { + esp_err_t errRc = esp_ble_tx_power_set(powerType, powerLevel); + if (errRc != ESP_OK) { + NIMBLE_LOGE(LOG_TAG, "esp_ble_tx_power_set: rc=%d", errRc); + } + + return errRc == ESP_OK; +} // setPowerLevel +# endif // !CONFIG_IDF_TARGET_ESP32P4 +# endif // ESP_PLATFORM + +/** + * @brief Set the transmission power. + * @param [in] dbm The power level to set in dBm. + * @return True if the power level was set successfully. + */ +bool NimBLEDevice::setPower(int8_t dbm, NimBLETxPowerType type) { +# ifdef ESP_PLATFORM +# ifdef CONFIG_IDF_TARGET_ESP32P4 + return false; // CONFIG_IDF_TARGET_ESP32P4 does not support esp_ble_tx_power_set +# else + if (dbm % 3 == 2) { + dbm++; // round up to the next multiple of 3 to be able to target 20dbm + } + + bool success = false; + esp_power_level_t espPwr = static_cast(dbm / 3 + ESP_PWR_LVL_N0); + if (type == NimBLETxPowerType::All) { + success = setPowerLevel(espPwr, ESP_BLE_PWR_TYPE_ADV); + success &= setPowerLevel(espPwr, ESP_BLE_PWR_TYPE_SCAN); + success &= setPowerLevel(espPwr, ESP_BLE_PWR_TYPE_DEFAULT); + } else if (type == NimBLETxPowerType::Advertise) { + success = setPowerLevel(espPwr, ESP_BLE_PWR_TYPE_ADV); + } else if (type == NimBLETxPowerType::Scan) { + success = setPowerLevel(espPwr, ESP_BLE_PWR_TYPE_SCAN); + } else if (type == NimBLETxPowerType::Connection) { + success = setPowerLevel(espPwr, ESP_BLE_PWR_TYPE_DEFAULT); + } + + return success; +# endif +# else + (void)type; // unused + NIMBLE_LOGD(LOG_TAG, ">> setPower: %d", dbm); + int rc = ble_phy_tx_power_set(dbm); + if (rc) { + NIMBLE_LOGE(LOG_TAG, "failed to set TX power, rc: %04x\n", rc); + return false; + } + + NIMBLE_LOGD(LOG_TAG, "TX power set to %d dBm\n", dbm); + return true; +# endif +} // setPower + +/** + * @brief Get the transmission power. + * @return The power level currently used in dbm or 0xFF on error. + */ +int NimBLEDevice::getPower(NimBLETxPowerType type) { +# ifdef ESP_PLATFORM +# ifdef CONFIG_IDF_TARGET_ESP32P4 + return 0xFF; // CONFIG_IDF_TARGET_ESP32P4 does not support esp_ble_tx_power_get +# else + esp_ble_power_type_t espPwr = type == NimBLETxPowerType::Advertise ? ESP_BLE_PWR_TYPE_ADV + : type == NimBLETxPowerType::Scan ? ESP_BLE_PWR_TYPE_SCAN + : ESP_BLE_PWR_TYPE_DEFAULT; + + int pwr = getPowerLevel(espPwr); + if (pwr < 0) { + NIMBLE_LOGE(LOG_TAG, "esp_ble_tx_power_get failed rc=%d", pwr); + return 0xFF; + } + + if (pwr < ESP_PWR_LVL_N0) { + return -3 * (ESP_PWR_LVL_N0 - pwr); + } + + if (pwr > ESP_PWR_LVL_N0) { + return std::min((pwr - ESP_PWR_LVL_N0) * 3, 20); + } + + return 0; +# endif +# else + (void)type; // unused + return ble_phy_tx_power_get(); +# endif +} // getPower + +/* -------------------------------------------------------------------------- */ +/* MTU FUNCTIONS */ +/* -------------------------------------------------------------------------- */ + +/** + * @brief Setup local mtu that will be used to negotiate mtu during request from client peer. + * @param [in] mtu Value to set local mtu: + * * This should be larger than 23 and lower or equal to BLE_ATT_MTU_MAX = 527. + * @return True if the mtu was set successfully. + */ +bool NimBLEDevice::setMTU(uint16_t mtu) { + int rc = ble_att_set_preferred_mtu(mtu); + if (rc != 0) { + NIMBLE_LOGE(LOG_TAG, "Could not set local mtu value to: %d, rc: %d", mtu, rc); + } + + return rc == 0; +} // setMTU + +/** + * @brief Get local MTU value set. + * @return The current preferred MTU setting. + */ +uint16_t NimBLEDevice::getMTU() { + return ble_att_preferred_mtu(); +} + +/* -------------------------------------------------------------------------- */ +/* BOND MANAGEMENT */ +/* -------------------------------------------------------------------------- */ + +# if CONFIG_BT_NIMBLE_ROLE_CENTRAL || CONFIG_BT_NIMBLE_ROLE_PERIPHERAL /** * @brief Gets the number of bonded peers stored */ -/*STATIC*/ int NimBLEDevice::getNumBonds() { ble_addr_t peer_id_addrs[MYNEWT_VAL(BLE_STORE_MAX_BONDS)]; - int num_peers, rc; - + int num_peers, rc; rc = ble_store_util_bonded_peers(&peer_id_addrs[0], &num_peers, MYNEWT_VAL(BLE_STORE_MAX_BONDS)); - if (rc !=0) { + if (rc != 0) { return 0; } return num_peers; } - /** * @brief Deletes all bonding information. - * @returns true on success, false on failure. + * @returns True on success. */ -/*STATIC*/ bool NimBLEDevice::deleteAllBonds() { int rc = ble_store_clear(); if (rc != 0) { @@ -580,36 +641,23 @@ bool NimBLEDevice::deleteAllBonds() { return true; } - /** * @brief Deletes a peer bond. * @param [in] address The address of the peer with which to delete bond info. - * @returns true on success. + * @returns True on success. */ -/*STATIC*/ -bool NimBLEDevice::deleteBond(const NimBLEAddress &address) { - ble_addr_t delAddr; - memcpy(&delAddr.val, address.getNative(),6); - delAddr.type = address.getType(); - - int rc = ble_gap_unpair(&delAddr); - if (rc != 0) { - return false; - } - - return true; +bool NimBLEDevice::deleteBond(const NimBLEAddress& address) { + return ble_gap_unpair(address.getBase()) == 0; } - /** * @brief Checks if a peer device is bonded. * @param [in] address The address to check for bonding. - * @returns true if bonded. + * @returns True if bonded. */ -/*STATIC*/ -bool NimBLEDevice::isBonded(const NimBLEAddress &address) { +bool NimBLEDevice::isBonded(const NimBLEAddress& address) { ble_addr_t peer_id_addrs[MYNEWT_VAL(BLE_STORE_MAX_BONDS)]; - int num_peers, rc; + int num_peers, rc; rc = ble_store_util_bonded_peers(&peer_id_addrs[0], &num_peers, MYNEWT_VAL(BLE_STORE_MAX_BONDS)); if (rc != 0) { @@ -618,7 +666,7 @@ bool NimBLEDevice::isBonded(const NimBLEAddress &address) { for (int i = 0; i < num_peers; i++) { NimBLEAddress storedAddr(peer_id_addrs[i]); - if(storedAddr == address) { + if (storedAddr == address) { return true; } } @@ -626,39 +674,35 @@ bool NimBLEDevice::isBonded(const NimBLEAddress &address) { return false; } - /** * @brief Get the address of a bonded peer device by index. * @param [in] index The index to retrieve the peer address of. - * @returns NimBLEAddress of the found bonded peer or nullptr if not found. + * @returns NimBLEAddress of the found bonded peer or null address if not found. */ -/*STATIC*/ NimBLEAddress NimBLEDevice::getBondedAddress(int index) { ble_addr_t peer_id_addrs[MYNEWT_VAL(BLE_STORE_MAX_BONDS)]; - int num_peers, rc; - + int num_peers, rc; rc = ble_store_util_bonded_peers(&peer_id_addrs[0], &num_peers, MYNEWT_VAL(BLE_STORE_MAX_BONDS)); - if (rc != 0) { - return nullptr; - } - - if (index > num_peers || index < 0) { - return nullptr; + if (rc != 0 || index > num_peers || index < 0) { + return NimBLEAddress{}; } return NimBLEAddress(peer_id_addrs[index]); } -#endif +# endif + +/* -------------------------------------------------------------------------- */ +/* WHITELIST */ +/* -------------------------------------------------------------------------- */ /** * @brief Checks if a peer device is whitelisted. * @param [in] address The address to check for in the whitelist. - * @returns true if the address is in the whitelist. + * @returns True if the address is in the whitelist. */ -/*STATIC*/ -bool NimBLEDevice::onWhiteList(const NimBLEAddress & address) { - for (auto &it : m_whiteList) { - if (it == address) { +bool NimBLEDevice::onWhiteList(const NimBLEAddress& address) { + for (const auto& addr : m_whiteList) { + if (addr == address) { return true; } } @@ -666,354 +710,447 @@ bool NimBLEDevice::onWhiteList(const NimBLEAddress & address) { return false; } - /** * @brief Add a peer address to the whitelist. * @param [in] address The address to add to the whitelist. - * @returns true if successful. + * @returns True if successful. */ -/*STATIC*/ -bool NimBLEDevice::whiteListAdd(const NimBLEAddress & address) { - if (NimBLEDevice::onWhiteList(address)) { - return true; - } - - m_whiteList.push_back(address); - std::vector wlVec; - wlVec.reserve(m_whiteList.size()); - - for (auto &it : m_whiteList) { - ble_addr_t wlAddr; - memcpy(&wlAddr.val, it.getNative(), 6); - wlAddr.type = it.getType(); - wlVec.push_back(wlAddr); - } - - int rc = ble_gap_wl_set(&wlVec[0], wlVec.size()); - if (rc != 0) { - NIMBLE_LOGE(LOG_TAG, "Failed adding to whitelist rc=%d", rc); - m_whiteList.pop_back(); - return false; +bool NimBLEDevice::whiteListAdd(const NimBLEAddress& address) { + if (!NimBLEDevice::onWhiteList(address)) { + m_whiteList.push_back(address); + int rc = ble_gap_wl_set(reinterpret_cast(&m_whiteList[0]), m_whiteList.size()); + if (rc != 0) { + NIMBLE_LOGE(LOG_TAG, "Failed adding to whitelist rc=%d", rc); + m_whiteList.pop_back(); + return false; + } } return true; } - /** * @brief Remove a peer address from the whitelist. * @param [in] address The address to remove from the whitelist. - * @returns true if successful. + * @returns True if successful. */ -/*STATIC*/ -bool NimBLEDevice::whiteListRemove(const NimBLEAddress & address) { - if (!NimBLEDevice::onWhiteList(address)) { - return true; - } - - std::vector wlVec; - wlVec.reserve(m_whiteList.size()); - - for (auto &it : m_whiteList) { - if (it != address) { - ble_addr_t wlAddr; - memcpy(&wlAddr.val, it.getNative(), 6); - wlAddr.type = it.getType(); - wlVec.push_back(wlAddr); - } - } - - int rc = ble_gap_wl_set(&wlVec[0], wlVec.size()); - if (rc != 0) { - NIMBLE_LOGE(LOG_TAG, "Failed removing from whitelist rc=%d", rc); - return false; - } - - // Don't remove from the list unless NimBLE returned success +bool NimBLEDevice::whiteListRemove(const NimBLEAddress& address) { for (auto it = m_whiteList.begin(); it < m_whiteList.end(); ++it) { - if ((*it) == address) { + if (*it == address) { m_whiteList.erase(it); - break; + int rc = ble_gap_wl_set(reinterpret_cast(&m_whiteList[0]), m_whiteList.size()); + if (rc != 0) { + m_whiteList.push_back(address); + NIMBLE_LOGE(LOG_TAG, "Failed removing from whitelist rc=%d", rc); + return false; + } + + std::vector(m_whiteList).swap(m_whiteList); } } return true; } - /** * @brief Gets the count of addresses in the whitelist. * @returns The number of addresses in the whitelist. */ -/*STATIC*/ size_t NimBLEDevice::getWhiteListCount() { return m_whiteList.size(); } - /** * @brief Gets the address at the vector index. * @param [in] index The vector index to retrieve the address from. - * @returns the NimBLEAddress at the whitelist index or nullptr if not found. + * @returns The NimBLEAddress at the whitelist index or null address if not found. */ -/*STATIC*/ NimBLEAddress NimBLEDevice::getWhiteListAddress(size_t index) { if (index > m_whiteList.size()) { NIMBLE_LOGE(LOG_TAG, "Invalid index; %u", index); - return nullptr; + return NimBLEAddress{}; } + return m_whiteList[index]; } +/* -------------------------------------------------------------------------- */ +/* STACK FUNCTIONS */ +/* -------------------------------------------------------------------------- */ /** - * @brief Host reset, we pass the message so we don't make calls until resynced. + * @brief Set the preferred default phy to use for connections. + * @param [in] txPhyMask TX PHY. Can be mask of following: + * - BLE_GAP_LE_PHY_1M_MASK + * - BLE_GAP_LE_PHY_2M_MASK + * - BLE_GAP_LE_PHY_CODED_MASK + * - BLE_GAP_LE_PHY_ANY_MASK + * @param [in] rxPhyMask RX PHY. Can be mask of following: + * - BLE_GAP_LE_PHY_1M_MASK + * - BLE_GAP_LE_PHY_2M_MASK + * - BLE_GAP_LE_PHY_CODED_MASK + * - BLE_GAP_LE_PHY_ANY_MASK + * @return True if successful. + */ +bool NimBLEDevice::setDefaultPhy(uint8_t txPhyMask, uint8_t rxPhyMask) { + int rc = ble_gap_set_prefered_default_le_phy(txPhyMask, rxPhyMask); + if (rc != 0) { + NIMBLE_LOGE(LOG_TAG, "Failed to set default phy; rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc)); + } + + return rc == 0; +} + +/** + * @brief Host reset, we pass the message so we don't make calls until re-synced. * @param [in] reason The reason code for the reset. */ -/* STATIC */ -void NimBLEDevice::onReset(int reason) -{ - if(!m_synced) { +void NimBLEDevice::onReset(int reason) { + if (!m_synced) { return; } m_synced = false; - NIMBLE_LOGE(LOG_TAG, "Resetting state; reason=%d, %s", reason, - NimBLEUtils::returnCodeToString(reason)); - -#if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER) - if(initialized) { - if(m_pScan != nullptr) { - m_pScan->onHostReset(); - } - } -#endif + NIMBLE_LOGE(LOG_TAG, "Host reset; reason=%d, %s", reason, NimBLEUtils::returnCodeToString(reason)); } // onReset - /** - * @brief Host resynced with controller, all clear to make calls to the stack. + * @brief Host synced with controller, all clear to make calls to the stack. */ -/* STATIC */ -void NimBLEDevice::onSync(void) -{ +void NimBLEDevice::onSync(void) { NIMBLE_LOGI(LOG_TAG, "NimBle host synced."); // This check is needed due to potentially being called multiple times in succession // If this happens, the call to scan start may get stuck or cause an advertising fault. - if(m_synced) { + if (m_synced) { return; } - /* Make sure we have proper identity address set (public preferred) */ + // Get the public and random address for the device. int rc = ble_hs_util_ensure_addr(0); + if (rc == 0) { + rc = ble_hs_util_ensure_addr(1); + } + if (rc != 0) { NIMBLE_LOGE(LOG_TAG, "error ensuring address; rc=%d", rc); return; } -#ifndef ESP_PLATFORM - rc = ble_hs_id_infer_auto(m_own_addr_type, &m_own_addr_type); + // start with using the public address if available, if not then use random. + rc = ble_hs_id_copy_addr(BLE_OWN_ADDR_PUBLIC, NULL, NULL); if (rc != 0) { - NIMBLE_LOGE(LOG_TAG, "error determining address type; rc=%d", rc); - return; + m_ownAddrType = BLE_OWN_ADDR_RANDOM; } -#endif - // Yield for housekeeping before returning to operations. + // Yield for housekeeping tasks before returning to operations. // Occasionally triggers exception without. - taskYIELD(); + ble_npl_time_delay(1); m_synced = true; - if(initialized) { -#if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER) - if(m_pScan != nullptr) { + if (m_initialized) { +# if CONFIG_BT_NIMBLE_ROLE_OBSERVER + if (m_pScan != nullptr) { m_pScan->onHostSync(); } -#endif +# endif -#if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER) - if(m_bleAdvertising != nullptr) { +# if CONFIG_BT_NIMBLE_ROLE_BROADCASTER + if (m_bleAdvertising != nullptr) { m_bleAdvertising->onHostSync(); } -#endif +# endif } } // onSync - /** * @brief The main host task. */ -/* STATIC */ -void NimBLEDevice::host_task(void *param) -{ +void NimBLEDevice::host_task(void* param) { NIMBLE_LOGI(LOG_TAG, "BLE Host Task Started"); - - /* This function will return only when nimble_port_stop() is executed */ - nimble_port_run(); - + nimble_port_run(); // This function will return only when nimble_port_stop() is executed nimble_port_freertos_deinit(); } // host_task - /** - * @brief Initialize the %BLE environment. + * @brief Initialize the BLE environment. * @param [in] deviceName The device name of the device. */ -/* STATIC */ -void NimBLEDevice::init(const std::string &deviceName) { - if(!initialized){ - int rc=0; -#ifdef ESP_PLATFORM - esp_err_t errRc = ESP_OK; +bool NimBLEDevice::init(const std::string& deviceName) { + if (!m_initialized) { +# ifdef ESP_PLATFORM -#ifdef CONFIG_ENABLE_ARDUINO_DEPENDS +# if defined(CONFIG_ENABLE_ARDUINO_DEPENDS) && SOC_BT_SUPPORTED // make sure the linker includes esp32-hal-bt.c so Arduino init doesn't release BLE memory. btStarted(); -#endif +# endif - errRc = nvs_flash_init(); - - if (errRc == ESP_ERR_NVS_NO_FREE_PAGES || errRc == ESP_ERR_NVS_NEW_VERSION_FOUND) { - ESP_ERROR_CHECK(nvs_flash_erase()); - errRc = nvs_flash_init(); + esp_err_t err = nvs_flash_init(); + if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) { + err = nvs_flash_erase(); + if (err == ESP_OK) { + err = nvs_flash_init(); + } } - ESP_ERROR_CHECK(errRc); + if (err != ESP_OK) { + NIMBLE_LOGE(LOG_TAG, "nvs_flash_init() failed; err=%d", err); + return false; + } -#if CONFIG_IDF_TARGET_ESP32 +# if CONFIG_IDF_TARGET_ESP32 esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT); -#endif +# endif -#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0) | !defined(CONFIG_NIMBLE_CPP_IDF) +# if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0) || !defined(CONFIG_NIMBLE_CPP_IDF) esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT(); -# if defined (CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S3) - bt_cfg.bluetooth_mode = ESP_BT_MODE_BLE; -# else - bt_cfg.mode = ESP_BT_MODE_BLE; +# if defined(CONFIG_IDF_TARGET_ESP32) + bt_cfg.mode = ESP_BT_MODE_BLE; bt_cfg.ble_max_conn = CONFIG_BT_NIMBLE_MAX_CONNECTIONS; -# endif +# elif defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S3) + bt_cfg.ble_max_act = CONFIG_BT_NIMBLE_MAX_CONNECTIONS; +# else + bt_cfg.nimble_max_connections = CONFIG_BT_NIMBLE_MAX_CONNECTIONS; +# endif -# ifdef CONFIG_BTDM_BLE_SCAN_DUPL - bt_cfg.normal_adv_size = m_scanDuplicateSize; - bt_cfg.scan_duplicate_type = m_scanFilterMode; +# if CONFIG_BTDM_BLE_SCAN_DUPL + bt_cfg.normal_adv_size = m_scanDuplicateSize; + bt_cfg.scan_duplicate_type = m_scanFilterMode; + bt_cfg.dup_list_refresh_period = m_scanDuplicateResetTime; +# elif CONFIG_BT_LE_SCAN_DUPL + bt_cfg.ble_ll_rsp_dup_list_count = m_scanDuplicateSize; + bt_cfg.ble_ll_adv_dup_list_count = m_scanDuplicateSize; +# endif + err = esp_bt_controller_init(&bt_cfg); + if (err != ESP_OK) { + NIMBLE_LOGE(LOG_TAG, "esp_bt_controller_init() failed; err=%d", err); + return false; + } + +# if CONFIG_BT_LE_SCAN_DUPL + int mode = (1UL << 4); // FILTER_DUPLICATE_EXCEPTION_FOR_MESH + switch (m_scanFilterMode) { + case 1: + mode |= (1UL << 3); // FILTER_DUPLICATE_ADVDATA + break; + case 2: + mode |= ((1UL << 2) | (1UL << 3)); // FILTER_DUPLICATE_ADDRESS | FILTER_DUPLICATE_ADVDATA + break; + default: + mode |= (1UL << 0) | (1UL << 2); // FILTER_DUPLICATE_PDUTYPE | FILTER_DUPLICATE_ADDRESS + } + + ble_vhci_disc_duplicate_mode_disable(0xFFFFFFFF); + ble_vhci_disc_duplicate_mode_enable(mode); + ble_vhci_disc_duplicate_set_max_cache_size(m_scanDuplicateSize); + ble_vhci_disc_duplicate_set_period_refresh_time(m_scanDuplicateResetTime); +# endif + + err = esp_bt_controller_enable(ESP_BT_MODE_BLE); + if (err != ESP_OK) { + NIMBLE_LOGE(LOG_TAG, "esp_bt_controller_enable() failed; err=%d", err); + return false; + } + +# if CONFIG_BT_NIMBLE_LEGACY_VHCI_ENABLE + err = esp_nimble_hci_init(); + if (err != ESP_OK) { + NIMBLE_LOGE(LOG_TAG, "esp_nimble_hci_init() failed; err=%d", err); + return false; + } +# endif # endif - ESP_ERROR_CHECK(esp_bt_controller_init(&bt_cfg)); - ESP_ERROR_CHECK(esp_bt_controller_enable(ESP_BT_MODE_BLE)); - ESP_ERROR_CHECK(esp_nimble_hci_init()); -# endif -#endif +# endif nimble_port_init(); // Setup callbacks for host events - ble_hs_cfg.reset_cb = NimBLEDevice::onReset; - ble_hs_cfg.sync_cb = NimBLEDevice::onSync; + ble_hs_cfg.reset_cb = NimBLEDevice::onReset; + ble_hs_cfg.sync_cb = NimBLEDevice::onSync; + ble_hs_cfg.store_status_cb = [](struct ble_store_status_event* event, void* arg) { + return m_pDeviceCallbacks->onStoreStatus(event, arg); + }; // Set initial security capabilities - ble_hs_cfg.sm_io_cap = BLE_HS_IO_NO_INPUT_OUTPUT; - ble_hs_cfg.sm_bonding = 0; - ble_hs_cfg.sm_mitm = 0; - ble_hs_cfg.sm_sc = 1; - ble_hs_cfg.sm_our_key_dist = BLE_SM_PAIR_KEY_DIST_ENC | BLE_SM_PAIR_KEY_DIST_ID; - ble_hs_cfg.sm_their_key_dist = BLE_SM_PAIR_KEY_DIST_ENC | BLE_SM_PAIR_KEY_DIST_ID; - - ble_hs_cfg.store_status_cb = ble_store_util_status_rr; /*TODO: Implement handler for this*/ - - // Set the device name. - rc = ble_svc_gap_device_name_set(deviceName.c_str()); - if (rc != 0) { - NIMBLE_LOGE(LOG_TAG, "ble_svc_gap_device_name_set() failed; rc=%d", rc); - } + ble_hs_cfg.sm_io_cap = BLE_HS_IO_NO_INPUT_OUTPUT; + ble_hs_cfg.sm_bonding = 0; + ble_hs_cfg.sm_mitm = 0; + ble_hs_cfg.sm_sc = 1; + ble_hs_cfg.sm_our_key_dist = BLE_SM_PAIR_KEY_DIST_ENC; + ble_hs_cfg.sm_their_key_dist = BLE_SM_PAIR_KEY_DIST_ENC; +# if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) + ble_hs_cfg.sm_our_key_dist |= BLE_SM_PAIR_KEY_DIST_ID; + ble_hs_cfg.sm_their_key_dist |= BLE_SM_PAIR_KEY_DIST_ID; +# endif + setDeviceName(deviceName); ble_store_config_init(); - nimble_port_freertos_init(NimBLEDevice::host_task); } // Wait for host and controller to sync before returning and accepting new tasks - while(!m_synced){ - taskYIELD(); + while (!m_synced) { + ble_npl_time_delay(1); } - initialized = true; // Set the initialization flag to ensure we are only initialized once. + m_initialized = true; // Set the initialization flag to ensure we are only initialized once. + return true; } // init - /** * @brief Shutdown the NimBLE stack/controller. - * @param [in] clearAll If true, deletes all server/advertising/scan/client objects after deinitializing. - * @note If clearAll is true when called, any references to the created objects become invalid. + * @param [in] clearAll If true, deletes all server/advertising/scan/client objects after de-initializing. + * @note If clearAll is true when called all objects created will be deleted and any references to the created objects become invalid. + * If clearAll is false, the objects will remain and can be used again after re-initializing the stack. + * If the stack was not already initialized, the objects created can be deleted when clearAll is true with no effect on the stack. */ -/* STATIC */ -void NimBLEDevice::deinit(bool clearAll) { - int ret = nimble_port_stop(); - if (ret == 0) { - nimble_port_deinit(); -#ifdef CONFIG_NIMBLE_CPP_IDF -# if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0) - ret = esp_nimble_hci_and_controller_deinit(); - if (ret != ESP_OK) { - NIMBLE_LOGE(LOG_TAG, "esp_nimble_hci_and_controller_deinit() failed with error: %d", ret); - } +bool NimBLEDevice::deinit(bool clearAll) { + int rc = 0; + if (m_initialized) { + rc = nimble_port_stop(); + if (rc == 0) { + nimble_port_deinit(); +# ifdef CONFIG_NIMBLE_CPP_IDF +# if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0) + rc = esp_nimble_hci_and_controller_deinit(); + if (rc != ESP_OK) { + NIMBLE_LOGE(LOG_TAG, "esp_nimble_hci_and_controller_deinit() failed with error: %d", rc); + } +# endif # endif -#endif - initialized = false; - m_synced = false; - - if(clearAll) { -#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) - if(NimBLEDevice::m_pServer != nullptr) { - delete NimBLEDevice::m_pServer; - NimBLEDevice::m_pServer = nullptr; - } -#endif - -#if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER) - if(NimBLEDevice::m_bleAdvertising != nullptr) { - delete NimBLEDevice::m_bleAdvertising; - NimBLEDevice::m_bleAdvertising = nullptr; - } -#endif - -#if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER) - if(NimBLEDevice::m_pScan != nullptr) { - delete NimBLEDevice::m_pScan; - NimBLEDevice::m_pScan= nullptr; - } -#endif - -#if defined( CONFIG_BT_NIMBLE_ROLE_CENTRAL) - for(auto &it : m_cList) { - deleteClient(it); - m_cList.clear(); - } -#endif - - m_ignoreList.clear(); + m_initialized = false; + m_synced = false; } } + + if (clearAll) { +# if CONFIG_BT_NIMBLE_ROLE_PERIPHERAL + if (NimBLEDevice::m_pServer != nullptr) { + delete NimBLEDevice::m_pServer; + NimBLEDevice::m_pServer = nullptr; + } +# if CONFIG_BT_NIMBLE_L2CAP_COC_MAX_NUM + if (NimBLEDevice::m_pL2CAPServer != nullptr) { + delete NimBLEDevice::m_pL2CAPServer; + NimBLEDevice::m_pL2CAPServer = nullptr; + } +# endif +# endif + +# if CONFIG_BT_NIMBLE_ROLE_BROADCASTER + if (NimBLEDevice::m_bleAdvertising != nullptr) { + delete NimBLEDevice::m_bleAdvertising; + NimBLEDevice::m_bleAdvertising = nullptr; + } +# endif + +# if CONFIG_BT_NIMBLE_ROLE_OBSERVER + if (NimBLEDevice::m_pScan != nullptr) { + delete NimBLEDevice::m_pScan; + NimBLEDevice::m_pScan = nullptr; + } +# endif + +# if CONFIG_BT_NIMBLE_ROLE_CENTRAL + for (auto clt : m_pClients) { + deleteClient(clt); + } +# endif + } + + return rc == 0; } // deinit -/** - * @brief Set the BLEDevice's name - * @param [in] deviceName The device name of the device. - */ -/* STATIC */ -void NimBLEDevice::setDeviceName(const std::string &deviceName) { - ble_svc_gap_device_name_set(deviceName.c_str()); -} // setDeviceName - - /** * @brief Check if the initialization is complete. * @return true if initialized. */ -/*STATIC*/ -bool NimBLEDevice::getInitialized() { - return initialized; +bool NimBLEDevice::isInitialized() { + return m_initialized; } // getInitialized +/* -------------------------------------------------------------------------- */ +/* ADDRESS MANAGEMENT */ +/* -------------------------------------------------------------------------- */ + +/** + * @brief Get our device address. + * @return A NimBLEAddress object with the currently used address, or a NULL address if not set. + */ +NimBLEAddress NimBLEDevice::getAddress() { + ble_addr_t addr{}; + uint8_t type = m_ownAddrType & 1; // input must be random or public, odd values are random + int rc = ble_hs_id_copy_addr(type, addr.val, NULL); + if (rc != 0) { + NIMBLE_LOGE(LOG_TAG, "No address, rc: %d", rc); + } else { + addr.type = type; + } + + return NimBLEAddress{addr}; +} // getAddress + +/** + * @brief Sets the address type to use. + * @param [in] type Bluetooth Device address type. + * The available types are defined as: + * * 0x00: BLE_OWN_ADDR_PUBLIC - Public address; Uses the hardware static address. + * * 0x01: BLE_OWN_ADDR_RANDOM - Random static address; Uses the hardware or generated random static address. + * * 0x02: BLE_OWN_ADDR_RPA_PUBLIC_DEFAULT - Resolvable private address, defaults to public if no RPA available. + * * 0x03: BLE_OWN_ADDR_RPA_RANDOM_DEFAULT - Resolvable private address, defaults to random static if no RPA available. + */ +bool NimBLEDevice::setOwnAddrType(uint8_t type) { + int rc = ble_hs_id_copy_addr(type & 1, NULL, NULL); // Odd values are random + if (rc != 0) { + NIMBLE_LOGE(LOG_TAG, "Unable to set address type %d, rc=%d", type, rc); + return false; + } + + m_ownAddrType = type; + +# if MYNEWT_VAL(BLE_HOST_BASED_PRIVACY) + if (type == BLE_OWN_ADDR_RPA_PUBLIC_DEFAULT || type == BLE_OWN_ADDR_RPA_RANDOM_DEFAULT) { + // esp32 controller does not support RPA so we must use the random static for calls to the stack + // the host will take care of the random private address generation/setting. + m_ownAddrType = BLE_OWN_ADDR_RANDOM; + rc = ble_hs_pvcy_rpa_config(NIMBLE_HOST_ENABLE_RPA); + } else { + rc = ble_hs_pvcy_rpa_config(NIMBLE_HOST_DISABLE_PRIVACY); + } +# endif + + return rc == 0; +} // setOwnAddrType + +/** + * @brief Set the device address to use. + * @param [in] addr The address to set. + * @return True if the address was set successfully. + * @details To use the address generated the address type must be set to random with `setOwnAddrType`. + */ +bool NimBLEDevice::setOwnAddr(const NimBLEAddress& addr) { + return setOwnAddr(addr.getBase()->val); +} // setOwnAddr + +/** + * @brief Set the device address to use. + * @param [in] addr The address to set. + * @return True if the address was set successfully. + * @details To use the address generated the address type must be set to random with `setOwnAddrType`. + */ +bool NimBLEDevice::setOwnAddr(const uint8_t* addr) { + int rc = ble_hs_id_set_rnd(addr); + if (rc != 0) { + NIMBLE_LOGE(LOG_TAG, "Failed to set address, rc=%d", rc); + return false; + } + + return true; +} // setOwnAddr + +/* -------------------------------------------------------------------------- */ +/* SECURITY */ +/* -------------------------------------------------------------------------- */ /** * @brief Set the authorization mode for this device. @@ -1021,15 +1158,13 @@ bool NimBLEDevice::getInitialized() { * @param mitm If true we are capable of man in the middle protection, false if not. * @param sc If true we will perform secure connection pairing, false we will use legacy pairing. */ -/*STATIC*/ void NimBLEDevice::setSecurityAuth(bool bonding, bool mitm, bool sc) { - NIMBLE_LOGD(LOG_TAG, "Setting bonding: %d, mitm: %d, sc: %d",bonding,mitm,sc); + NIMBLE_LOGD(LOG_TAG, "Setting bonding: %d, mitm: %d, sc: %d", bonding, mitm, sc); ble_hs_cfg.sm_bonding = bonding; - ble_hs_cfg.sm_mitm = mitm; - ble_hs_cfg.sm_sc = sc; + ble_hs_cfg.sm_mitm = mitm; + ble_hs_cfg.sm_sc = sc; } // setSecurityAuth - /** * @brief Set the authorization mode for this device. * @param auth_req A bitmap indicating what modes are supported.\n @@ -1039,14 +1174,12 @@ void NimBLEDevice::setSecurityAuth(bool bonding, bool mitm, bool sc) { * * 0x08 BLE_SM_PAIR_AUTHREQ_SC * * 0x10 BLE_SM_PAIR_AUTHREQ_KEYPRESS - not yet supported. */ -/*STATIC*/ void NimBLEDevice::setSecurityAuth(uint8_t auth_req) { - NimBLEDevice::setSecurityAuth((auth_req & BLE_SM_PAIR_AUTHREQ_BOND)>0, - (auth_req & BLE_SM_PAIR_AUTHREQ_MITM)>0, - (auth_req & BLE_SM_PAIR_AUTHREQ_SC)>0); + NimBLEDevice::setSecurityAuth(auth_req & BLE_SM_PAIR_AUTHREQ_BOND, + auth_req & BLE_SM_PAIR_AUTHREQ_MITM, + auth_req & BLE_SM_PAIR_AUTHREQ_SC); } // setSecurityAuth - /** * @brief Set the Input/Output capabilities of this device. * @param iocap One of the following values: @@ -1056,220 +1189,159 @@ void NimBLEDevice::setSecurityAuth(uint8_t auth_req) { * * 0x03 BLE_HS_IO_NO_INPUT_OUTPUT NoInputNoOutput IO capability * * 0x04 BLE_HS_IO_KEYBOARD_DISPLAY KeyboardDisplay Only IO capability */ -/*STATIC*/ void NimBLEDevice::setSecurityIOCap(uint8_t iocap) { ble_hs_cfg.sm_io_cap = iocap; } // setSecurityIOCap - /** * @brief If we are the initiator of the security procedure this sets the keys we will distribute. - * @param init_key A bitmap indicating which keys to distribute during pairing.\n + * @param initKey A bitmap indicating which keys to distribute during pairing.\n * The available bits are defined as: * * 0x01: BLE_SM_PAIR_KEY_DIST_ENC - Distribute the encryption key. * * 0x02: BLE_SM_PAIR_KEY_DIST_ID - Distribute the ID key (IRK). * * 0x04: BLE_SM_PAIR_KEY_DIST_SIGN * * 0x08: BLE_SM_PAIR_KEY_DIST_LINK */ -/*STATIC*/ -void NimBLEDevice::setSecurityInitKey(uint8_t init_key) { - ble_hs_cfg.sm_our_key_dist = init_key; +void NimBLEDevice::setSecurityInitKey(uint8_t initKey) { + ble_hs_cfg.sm_our_key_dist = initKey; } // setsSecurityInitKey - /** * @brief Set the keys we are willing to accept during pairing. - * @param resp_key A bitmap indicating which keys to accept during pairing. + * @param respKey A bitmap indicating which keys to accept during pairing. * The available bits are defined as: * * 0x01: BLE_SM_PAIR_KEY_DIST_ENC - Accept the encryption key. * * 0x02: BLE_SM_PAIR_KEY_DIST_ID - Accept the ID key (IRK). * * 0x04: BLE_SM_PAIR_KEY_DIST_SIGN * * 0x08: BLE_SM_PAIR_KEY_DIST_LINK */ -/*STATIC*/ -void NimBLEDevice::setSecurityRespKey(uint8_t resp_key) { - ble_hs_cfg.sm_their_key_dist = resp_key; +void NimBLEDevice::setSecurityRespKey(uint8_t respKey) { + ble_hs_cfg.sm_their_key_dist = respKey; } // setsSecurityRespKey - /** * @brief Set the passkey the server will ask for when pairing. - * @param [in] pin The passkey to use. + * @param [in] passkey The passkey to use. */ -/*STATIC*/ -void NimBLEDevice::setSecurityPasskey(uint32_t pin) { - m_passkey = pin; +void NimBLEDevice::setSecurityPasskey(uint32_t passkey) { + m_passkey = passkey; } // setSecurityPasskey - /** * @brief Get the current passkey used for pairing. * @return The current passkey. */ -/*STATIC*/ uint32_t NimBLEDevice::getSecurityPasskey() { return m_passkey; } // getSecurityPasskey - -#ifdef ESP_PLATFORM -/** - * @brief Set the own address type. - * @param [in] own_addr_type Own Bluetooth Device address type.\n - * The available bits are defined as: - * * 0x00: BLE_OWN_ADDR_PUBLIC - * * 0x01: BLE_OWN_ADDR_RANDOM - * * 0x02: BLE_OWN_ADDR_RPA_PUBLIC_DEFAULT - * * 0x03: BLE_OWN_ADDR_RPA_RANDOM_DEFAULT - * @param [in] useNRPA If true, and address type is random, uses a non-resolvable random address. - */ -/*STATIC*/ -void NimBLEDevice::setOwnAddrType(uint8_t own_addr_type, bool useNRPA) { - m_own_addr_type = own_addr_type; - switch (own_addr_type) { -#ifdef CONFIG_IDF_TARGET_ESP32 - case BLE_OWN_ADDR_PUBLIC: - ble_hs_pvcy_rpa_config(NIMBLE_HOST_DISABLE_PRIVACY); - break; -#endif - case BLE_OWN_ADDR_RANDOM: - setSecurityInitKey(BLE_SM_PAIR_KEY_DIST_ENC | BLE_SM_PAIR_KEY_DIST_ID); -#ifdef CONFIG_IDF_TARGET_ESP32 - ble_hs_pvcy_rpa_config(useNRPA ? NIMBLE_HOST_ENABLE_NRPA : NIMBLE_HOST_ENABLE_RPA); -#endif - break; - case BLE_OWN_ADDR_RPA_PUBLIC_DEFAULT: - case BLE_OWN_ADDR_RPA_RANDOM_DEFAULT: - setSecurityInitKey(BLE_SM_PAIR_KEY_DIST_ENC | BLE_SM_PAIR_KEY_DIST_ID); -#ifdef CONFIG_IDF_TARGET_ESP32 - ble_hs_pvcy_rpa_config(NIMBLE_HOST_ENABLE_RPA); -#endif - break; - } -} // setOwnAddrType -#endif - /** * @brief Start the connection securing and authorization for this connection. - * @param conn_id The connection id of the peer device. - * @returns NimBLE stack return code, 0 = success. + * @param connHandle The connection handle of the peer device. + * @param rcPtr Optional pointer to pass the return code to the caller. + * @returns True if successfully started, success = 0 or BLE_HS_EALREADY. */ -/* STATIC */ -int NimBLEDevice::startSecurity(uint16_t conn_id) { - int rc = ble_gap_security_initiate(conn_id); - if(rc != 0){ +bool NimBLEDevice::startSecurity(uint16_t connHandle, int* rcPtr) { + int rc = ble_gap_security_initiate(connHandle); + if (rc != 0) { NIMBLE_LOGE(LOG_TAG, "ble_gap_security_initiate: rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc)); } - - return rc; + if (rcPtr) { + *rcPtr = rc; + } + return rc == 0 || rc == BLE_HS_EALREADY; } // startSecurity - +# if CONFIG_BT_NIMBLE_ROLE_CENTRAL || CONFIG_BT_NIMBLE_ROLE_PERIPHERAL /** - * @brief Inject the provided passkey into the Security Manager - * @param [in] peerInfo Connection information for the peer - * @param [in] pin The 6-digit pin to inject + * @brief Inject the provided passkey into the Security Manager. + * @param [in] peerInfo Connection information for the peer. + * @param [in] passkey The 6-digit passkey to inject. * @return true if the passkey was injected successfully. */ -bool NimBLEDevice::injectPassKey(const NimBLEConnInfo& peerInfo, uint32_t pin) { - int rc = 0; - struct ble_sm_io pkey = {0,0}; - - pkey.action = BLE_SM_IOACT_INPUT; - pkey.passkey = pin; - - rc = ble_sm_inject_io(peerInfo.getConnHandle(), &pkey); +bool NimBLEDevice::injectPassKey(const NimBLEConnInfo& peerInfo, uint32_t passkey) { + ble_sm_io pkey{.action = BLE_SM_IOACT_INPUT, .passkey = passkey}; + int rc = ble_sm_inject_io(peerInfo.getConnHandle(), &pkey); NIMBLE_LOGD(LOG_TAG, "BLE_SM_IOACT_INPUT; ble_sm_inject_io result: %d", rc); return rc == 0; } - /** - * @brief Inject the provided numeric comparison response into the Security Manager - * @param [in] peerInfo Connection information for the peer - * @param [in] accept Whether the user confirmed or declined the comparison + * @brief Inject the provided numeric comparison response into the Security Manager. + * @param [in] peerInfo Connection information for the peer. + * @param [in] accept Whether the user confirmed or declined the comparison. */ -bool NimBLEDevice::injectConfirmPIN(const NimBLEConnInfo& peerInfo, bool accept) { - int rc = 0; - struct ble_sm_io pkey = {0,0}; - - pkey.action = BLE_SM_IOACT_NUMCMP; - pkey.numcmp_accept = accept; - - rc = ble_sm_inject_io(peerInfo.getConnHandle(), &pkey); +bool NimBLEDevice::injectConfirmPasskey(const NimBLEConnInfo& peerInfo, bool accept) { + ble_sm_io pkey{.action = BLE_SM_IOACT_NUMCMP, .numcmp_accept = accept}; + int rc = ble_sm_inject_io(peerInfo.getConnHandle(), &pkey); NIMBLE_LOGD(LOG_TAG, "BLE_SM_IOACT_NUMCMP; ble_sm_inject_io result: %d", rc); return rc == 0; } +# endif // CONFIG_BT_NIMBLE_ROLE_CENTRAL || CONFIG_BT_NIMBLE_ROLE_PERIPHERAL +/* -------------------------------------------------------------------------- */ +/* UTILITIES */ +/* -------------------------------------------------------------------------- */ /** - * @brief Check if the device address is on our ignore list. - * @param [in] address The address to look for. - * @return True if ignoring. + * @brief Set the BLEDevice name. + * @param [in] deviceName The name to set. */ -/*STATIC*/ -bool NimBLEDevice::isIgnored(const NimBLEAddress &address) { - for(auto &it : m_ignoreList) { - if(it.equals(address)){ - return true; - } +bool NimBLEDevice::setDeviceName(const std::string& deviceName) { + int rc = ble_svc_gap_device_name_set(deviceName.c_str()); + if (rc != 0) { + NIMBLE_LOGE(LOG_TAG, "Device name not set - too long"); + return false; } - return false; -} - - -/** - * @brief Add a device to the ignore list. - * @param [in] address The address of the device we want to ignore. - */ -/*STATIC*/ -void NimBLEDevice::addIgnored(const NimBLEAddress &address) { - m_ignoreList.push_back(address); -} - - -/** - * @brief Remove a device from the ignore list. - * @param [in] address The address of the device we want to remove from the list. - */ -/*STATIC*/ -void NimBLEDevice::removeIgnored(const NimBLEAddress &address) { - for(auto it = m_ignoreList.begin(); it != m_ignoreList.end(); ++it) { - if((*it).equals(address)){ - m_ignoreList.erase(it); - return; - } - } -} - + return true; +} // setDeviceName /** * @brief Set a custom callback for gap events. * @param [in] handler The function to call when gap events occur. + * @returns */ -/*STATIC*/ -void NimBLEDevice::setCustomGapHandler(gap_event_handler handler) { - m_customGapHandler = handler; - int rc = ble_gap_event_listener_register(&m_listener, m_customGapHandler, NULL); - if(rc == BLE_HS_EALREADY){ +bool NimBLEDevice::setCustomGapHandler(gap_event_handler handler) { + int rc = ble_gap_event_listener_register(&m_listener, handler, NULL); + if (rc == BLE_HS_EALREADY) { NIMBLE_LOGI(LOG_TAG, "Already listening to GAP events."); + return true; } else if (rc != 0) { NIMBLE_LOGE(LOG_TAG, "ble_gap_event_listener_register: rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc)); } + return rc == 0; } // setCustomGapHandler -#if CONFIG_NIMBLE_CPP_DEBUG_ASSERT_ENABLED || __DOXYGEN__ +/** + * @brief Return a string representation of the address of this device. + * @return A string representation of this device address. + */ +std::string NimBLEDevice::toString() { + return getAddress().toString(); +} // toString + +# if CONFIG_NIMBLE_CPP_DEBUG_ASSERT_ENABLED || __DOXYGEN__ /** * @brief Debug assert - weak function. * @param [in] file The file where the assert occurred. * @param [in] line The line number where the assert occurred. */ -void nimble_cpp_assert(const char *file, unsigned line) { - NIMBLE_LOGC("", "Assertion failed at %s:%u\n", file, line); +void nimble_cpp_assert(const char* file, unsigned line) { + console_printf("Assertion failed at %s:%u\n", file, line); + ble_npl_time_delay(10); abort(); } -#endif // CONFIG_NIMBLE_CPP_DEBUG_ASSERT_ENABLED +# endif // CONFIG_NIMBLE_CPP_DEBUG_ASSERT_ENABLED -#endif // CONFIG_BT_ENABLED \ No newline at end of file +void NimBLEDevice::setDeviceCallbacks(NimBLEDeviceCallbacks* cb) { + m_pDeviceCallbacks = cb ? cb : &defaultDeviceCallbacks; +} + +int NimBLEDeviceCallbacks::onStoreStatus(struct ble_store_status_event* event, void* arg) { + NIMBLE_LOGD("NimBLEDeviceCallbacks", "onStoreStatus: default"); + return ble_store_util_status_rr(event, arg); +} + +#endif // CONFIG_BT_ENABLED diff --git a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEDevice.h b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEDevice.h index 64bd4ed34..df89852c8 100644 --- a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEDevice.h +++ b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEDevice.h @@ -1,243 +1,341 @@ /* - * NimBLEDevice.h + * Copyright 2020-2025 Ryan Powell and + * esp-nimble-cpp, NimBLE-Arduino contributors. * - * Created: on Jan 24 2020 - * Author H2zero + * 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 * - * Originally: + * http://www.apache.org/licenses/LICENSE-2.0 * - * BLEDevice.h - * - * Created on: Mar 16, 2017 - * Author: kolban + * 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 MAIN_NIMBLEDEVICE_H_ -#define MAIN_NIMBLEDEVICE_H_ +#ifndef NIMBLE_CPP_DEVICE_H_ +#define NIMBLE_CPP_DEVICE_H_ #include "nimconfig.h" -#if defined(CONFIG_BT_ENABLED) - -#if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER) -#include "NimBLEScan.h" -#endif - -#if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER) -# if CONFIG_BT_NIMBLE_EXT_ADV -# include "NimBLEExtAdvertising.h" -# else -# include "NimBLEAdvertising.h" +#if CONFIG_BT_ENABLED +# ifdef ESP_PLATFORM +# ifndef CONFIG_IDF_TARGET_ESP32P4 +# include # endif -#endif +# endif -#if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL) -#include "NimBLEClient.h" -#endif +# if defined(CONFIG_NIMBLE_CPP_IDF) +# include +# else +# include +# endif -#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) -#include "NimBLEServer.h" -#endif +/**** FIX COMPILATION ****/ +# undef min +# undef max +/**************************/ -#include "NimBLEUtils.h" -#include "NimBLEAddress.h" +# include +# include -#ifdef ESP_PLATFORM -# include "esp_bt.h" -#endif +# if CONFIG_BT_NIMBLE_ROLE_CENTRAL +# include +class NimBLEClient; +# endif -#include -#include -#include +# if CONFIG_BT_NIMBLE_ROLE_OBSERVER +class NimBLEScan; +# endif -#define BLEDevice NimBLEDevice -#define BLEClient NimBLEClient -#define BLERemoteService NimBLERemoteService -#define BLERemoteCharacteristic NimBLERemoteCharacteristic -#define BLERemoteDescriptor NimBLERemoteDescriptor -#define BLEAdvertisedDevice NimBLEAdvertisedDevice -#define BLEScan NimBLEScan -#define BLEUUID NimBLEUUID -#define BLESecurity NimBLESecurity -#define BLESecurityCallbacks NimBLESecurityCallbacks -#define BLEAddress NimBLEAddress -#define BLEUtils NimBLEUtils -#define BLEClientCallbacks NimBLEClientCallbacks -#define BLEAdvertisedDeviceCallbacks NimBLEScanCallbacks -#define BLEScanResults NimBLEScanResults -#define BLEServer NimBLEServer -#define BLEService NimBLEService -#define BLECharacteristic NimBLECharacteristic -#define BLEAdvertising NimBLEAdvertising -#define BLEServerCallbacks NimBLEServerCallbacks -#define BLECharacteristicCallbacks NimBLECharacteristicCallbacks -#define BLEAdvertisementData NimBLEAdvertisementData -#define BLEDescriptor NimBLEDescriptor -#define BLE2902 NimBLE2902 -#define BLE2904 NimBLE2904 -#define BLEDescriptorCallbacks NimBLEDescriptorCallbacks -#define BLEBeacon NimBLEBeacon -#define BLEEddystoneTLM NimBLEEddystoneTLM -#define BLEEddystoneURL NimBLEEddystoneURL -#define BLEConnInfo NimBLEConnInfo +# if CONFIG_BT_NIMBLE_ROLE_BROADCASTER +# if CONFIG_BT_NIMBLE_EXT_ADV +class NimBLEExtAdvertising; +# else +class NimBLEAdvertising; +# endif +# endif -#ifdef CONFIG_BT_NIMBLE_MAX_CONNECTIONS -#define NIMBLE_MAX_CONNECTIONS CONFIG_BT_NIMBLE_MAX_CONNECTIONS -#else -#define NIMBLE_MAX_CONNECTIONS CONFIG_NIMBLE_MAX_CONNECTIONS -#endif +# if CONFIG_BT_NIMBLE_ROLE_PERIPHERAL +class NimBLEServer; +# if CONFIG_BT_NIMBLE_L2CAP_COC_MAX_NUM > 0 +class NimBLEL2CAPServer; +# endif +# endif -typedef int (*gap_event_handler)(ble_gap_event *event, void *arg); +# if CONFIG_BT_NIMBLE_ROLE_PERIPHERAL || CONFIG_BT_NIMBLE_ROLE_CENTRAL +class NimBLEConnInfo; +# endif -extern "C" void ble_store_config_init(void); +class NimBLEAddress; +class NimBLEDeviceCallbacks; + +# define BLEDevice NimBLEDevice +# define BLEClient NimBLEClient +# define BLERemoteService NimBLERemoteService +# define BLERemoteCharacteristic NimBLERemoteCharacteristic +# define BLERemoteDescriptor NimBLERemoteDescriptor +# define BLEAdvertisedDevice NimBLEAdvertisedDevice +# define BLEScan NimBLEScan +# define BLEUUID NimBLEUUID +# define BLEAddress NimBLEAddress +# define BLEUtils NimBLEUtils +# define BLEClientCallbacks NimBLEClientCallbacks +# define BLEAdvertisedDeviceCallbacks NimBLEScanCallbacks +# define BLEScanResults NimBLEScanResults +# define BLEServer NimBLEServer +# define BLEService NimBLEService +# define BLECharacteristic NimBLECharacteristic +# define BLEAdvertising NimBLEAdvertising +# define BLEServerCallbacks NimBLEServerCallbacks +# define BLECharacteristicCallbacks NimBLECharacteristicCallbacks +# define BLEAdvertisementData NimBLEAdvertisementData +# define BLEDescriptor NimBLEDescriptor +# define BLE2904 NimBLE2904 +# define BLEDescriptorCallbacks NimBLEDescriptorCallbacks +# define BLEBeacon NimBLEBeacon +# define BLEEddystoneTLM NimBLEEddystoneTLM +# define BLEEddystoneURL NimBLEEddystoneURL +# define BLEConnInfo NimBLEConnInfo +# define BLEL2CAPServer NimBLEL2CAPServer +# define BLEL2CAPService NimBLEL2CAPService +# define BLEL2CAPServiceCallbacks NimBLEL2CAPServiceCallbacks +# define BLEL2CAPClient NimBLEL2CAPClient +# define BLEL2CAPClientCallbacks NimBLEL2CAPClientCallbacks +# define BLEL2CAPChannel NimBLEL2CAPChannel +# define BLEL2CAPChannelCallbacks NimBLEL2CAPChannelCallbacks + +# ifdef CONFIG_BT_NIMBLE_MAX_CONNECTIONS +# define NIMBLE_MAX_CONNECTIONS CONFIG_BT_NIMBLE_MAX_CONNECTIONS +# else +# define NIMBLE_MAX_CONNECTIONS CONFIG_NIMBLE_MAX_CONNECTIONS +# endif + +enum class NimBLETxPowerType { All = 0, Advertise = 1, Scan = 2, Connection = 3 }; + +typedef int (*gap_event_handler)(ble_gap_event* event, void* arg); /** - * @brief A model of a %BLE Device from which all the BLE roles are created. + * @brief A model of a BLE Device from which all the BLE roles are created. */ class NimBLEDevice { -public: - static void init(const std::string &deviceName); - static void deinit(bool clearAll = false); - static void setDeviceName(const std::string &deviceName); - static bool getInitialized(); - static NimBLEAddress getAddress(); - static std::string toString(); - static bool whiteListAdd(const NimBLEAddress & address); - static bool whiteListRemove(const NimBLEAddress & address); - static bool onWhiteList(const NimBLEAddress & address); - static size_t getWhiteListCount(); - static NimBLEAddress getWhiteListAddress(size_t index); + public: + static bool init(const std::string& deviceName); + static bool deinit(bool clearAll = false); + static bool setDeviceName(const std::string& deviceName); + static bool isInitialized(); + static NimBLEAddress getAddress(); + static std::string toString(); + static bool whiteListAdd(const NimBLEAddress& address); + static bool whiteListRemove(const NimBLEAddress& address); + static bool onWhiteList(const NimBLEAddress& address); + static size_t getWhiteListCount(); + static NimBLEAddress getWhiteListAddress(size_t index); + static bool setOwnAddrType(uint8_t type); + static bool setOwnAddr(const NimBLEAddress& addr); + static bool setOwnAddr(const uint8_t* addr); + static void setDeviceCallbacks(NimBLEDeviceCallbacks* cb); + static void setScanDuplicateCacheSize(uint16_t cacheSize); + static void setScanFilterMode(uint8_t type); + static void setScanDuplicateCacheResetTime(uint16_t time); + static bool setCustomGapHandler(gap_event_handler handler); + static void setSecurityAuth(bool bonding, bool mitm, bool sc); + static void setSecurityAuth(uint8_t auth); + static void setSecurityIOCap(uint8_t iocap); + static void setSecurityInitKey(uint8_t initKey); + static void setSecurityRespKey(uint8_t respKey); + static void setSecurityPasskey(uint32_t passKey); + static uint32_t getSecurityPasskey(); + static bool startSecurity(uint16_t connHandle, int* rcPtr = nullptr); + static bool setMTU(uint16_t mtu); + static uint16_t getMTU(); + static void onReset(int reason); + static void onSync(void); + static void host_task(void* param); + static int getPower(NimBLETxPowerType type = NimBLETxPowerType::All); + static bool setPower(int8_t dbm, NimBLETxPowerType type = NimBLETxPowerType::All); + static bool setDefaultPhy(uint8_t txPhyMask, uint8_t rxPhyMask); -#if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER) - static NimBLEScan* getScan(); -#endif +# ifdef ESP_PLATFORM +# ifndef CONFIG_IDF_TARGET_ESP32P4 + static esp_power_level_t getPowerLevel(esp_ble_power_type_t powerType = ESP_BLE_PWR_TYPE_DEFAULT); + static bool setPowerLevel(esp_power_level_t powerLevel, esp_ble_power_type_t powerType = ESP_BLE_PWR_TYPE_DEFAULT); +# endif +# endif -#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) - static NimBLEServer* createServer(); - static NimBLEServer* getServer(); -#endif +# if CONFIG_BT_NIMBLE_ROLE_OBSERVER + static NimBLEScan* getScan(); +# endif -#ifdef ESP_PLATFORM - static void setPower(esp_power_level_t powerLevel, esp_ble_power_type_t powerType=ESP_BLE_PWR_TYPE_DEFAULT); - static int getPower(esp_ble_power_type_t powerType=ESP_BLE_PWR_TYPE_DEFAULT); - static void setOwnAddrType(uint8_t own_addr_type, bool useNRPA=false); - static void setScanDuplicateCacheSize(uint16_t cacheSize); - static void setScanFilterMode(uint8_t type); -#else - static void setPower(int dbm); - static int getPower(); -#endif +# if CONFIG_BT_NIMBLE_ROLE_PERIPHERAL + static NimBLEServer* createServer(); + static NimBLEServer* getServer(); +# if CONFIG_BT_NIMBLE_L2CAP_COC_MAX_NUM > 0 + static NimBLEL2CAPServer* createL2CAPServer(); + static NimBLEL2CAPServer* getL2CAPServer(); +# endif +# endif - static void setCustomGapHandler(gap_event_handler handler); - static void setSecurityAuth(bool bonding, bool mitm, bool sc); - static void setSecurityAuth(uint8_t auth_req); - static void setSecurityIOCap(uint8_t iocap); - static void setSecurityInitKey(uint8_t init_key); - static void setSecurityRespKey(uint8_t init_key); - static void setSecurityPasskey(uint32_t pin); - static uint32_t getSecurityPasskey(); - static int startSecurity(uint16_t conn_id); - static bool injectConfirmPIN(const NimBLEConnInfo& peerInfo, bool accept); - static bool injectPassKey(const NimBLEConnInfo& peerInfo, uint32_t pin); - static int setMTU(uint16_t mtu); - static uint16_t getMTU(); - static bool isIgnored(const NimBLEAddress &address); - static void addIgnored(const NimBLEAddress &address); - static void removeIgnored(const NimBLEAddress &address); +# if CONFIG_BT_NIMBLE_ROLE_PERIPHERAL || CONFIG_BT_NIMBLE_ROLE_CENTRAL + static bool injectConfirmPasskey(const NimBLEConnInfo& peerInfo, bool accept); + static bool injectPassKey(const NimBLEConnInfo& peerInfo, uint32_t pin); +# endif -#if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER) +# if CONFIG_BT_NIMBLE_ROLE_BROADCASTER # if CONFIG_BT_NIMBLE_EXT_ADV static NimBLEExtAdvertising* getAdvertising(); - static bool startAdvertising(uint8_t inst_id, - int duration = 0, - int max_events = 0); - static bool stopAdvertising(uint8_t inst_id); + static bool startAdvertising(uint8_t instId, int duration = 0, int maxEvents = 0); + static bool stopAdvertising(uint8_t instId); static bool stopAdvertising(); # endif # if !CONFIG_BT_NIMBLE_EXT_ADV || defined(_DOXYGEN_) - static NimBLEAdvertising* getAdvertising(); - static bool startAdvertising(uint32_t duration = 0); - static bool stopAdvertising(); + static NimBLEAdvertising* getAdvertising(); + static bool startAdvertising(uint32_t duration = 0); + static bool stopAdvertising(); # endif -#endif +# endif -#if defined( CONFIG_BT_NIMBLE_ROLE_CENTRAL) - static NimBLEClient* createClient(NimBLEAddress peerAddress = NimBLEAddress("")); - static bool deleteClient(NimBLEClient* pClient); - static NimBLEClient* getClientByID(uint16_t conn_id); - static NimBLEClient* getClientByPeerAddress(const NimBLEAddress &peer_addr); - static NimBLEClient* getDisconnectedClient(); - static size_t getClientListSize(); - static std::list* getClientList(); -#endif +# if CONFIG_BT_NIMBLE_ROLE_CENTRAL + static NimBLEClient* createClient(); + static NimBLEClient* createClient(const NimBLEAddress& peerAddress); + static bool deleteClient(NimBLEClient* pClient); + static NimBLEClient* getClientByHandle(uint16_t connHandle); + static NimBLEClient* getClientByPeerAddress(const NimBLEAddress& peerAddress); + static NimBLEClient* getDisconnectedClient(); + static size_t getCreatedClientCount(); + static std::vector getConnectedClients(); +# endif -#if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL) || defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) - static bool deleteBond(const NimBLEAddress &address); - static int getNumBonds(); - static bool isBonded(const NimBLEAddress &address); - static bool deleteAllBonds(); - static NimBLEAddress getBondedAddress(int index); -#endif +# if CONFIG_BT_NIMBLE_ROLE_CENTRAL || CONFIG_BT_NIMBLE_ROLE_PERIPHERAL + static bool deleteBond(const NimBLEAddress& address); + static int getNumBonds(); + static bool isBonded(const NimBLEAddress& address); + static bool deleteAllBonds(); + static NimBLEAddress getBondedAddress(int index); +# endif -private: -#if defined( CONFIG_BT_NIMBLE_ROLE_CENTRAL) + private: + static bool m_synced; + static bool m_initialized; + static uint32_t m_passkey; + static ble_gap_event_listener m_listener; + static uint8_t m_ownAddrType; + static std::vector m_whiteList; + static NimBLEDeviceCallbacks* m_pDeviceCallbacks; + static NimBLEDeviceCallbacks defaultDeviceCallbacks; + +# if CONFIG_BT_NIMBLE_ROLE_OBSERVER + static NimBLEScan* m_pScan; +# endif + +# if CONFIG_BT_NIMBLE_ROLE_PERIPHERAL + static NimBLEServer* m_pServer; +# if CONFIG_BT_NIMBLE_L2CAP_COC_MAX_NUM > 0 + static NimBLEL2CAPServer* m_pL2CAPServer; +# endif +# endif + +# if CONFIG_BT_NIMBLE_ROLE_BROADCASTER +# if CONFIG_BT_NIMBLE_EXT_ADV + static NimBLEExtAdvertising* m_bleAdvertising; +# else + static NimBLEAdvertising* m_bleAdvertising; +# endif +# endif + +# if CONFIG_BT_NIMBLE_ROLE_CENTRAL + static std::array m_pClients; +# endif + +# ifdef ESP_PLATFORM +# if CONFIG_BTDM_BLE_SCAN_DUPL || CONFIG_BT_LE_SCAN_DUPL + static uint16_t m_scanDuplicateSize; + static uint8_t m_scanFilterMode; + static uint16_t m_scanDuplicateResetTime; +# endif +# endif + +# if CONFIG_BT_NIMBLE_ROLE_CENTRAL friend class NimBLEClient; -#endif +# endif -#if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER) +# if CONFIG_BT_NIMBLE_ROLE_OBSERVER friend class NimBLEScan; -#endif +# endif -#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) +# if CONFIG_BT_NIMBLE_ROLE_PERIPHERAL friend class NimBLEServer; friend class NimBLECharacteristic; -#endif +# endif -#if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER) +# if CONFIG_BT_NIMBLE_ROLE_BROADCASTER friend class NimBLEAdvertising; # if CONFIG_BT_NIMBLE_EXT_ADV friend class NimBLEExtAdvertising; friend class NimBLEExtAdvertisement; # endif -#endif - - static void onReset(int reason); - static void onSync(void); - static void host_task(void *param); - static bool m_synced; - -#if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER) - static NimBLEScan* m_pScan; -#endif - -#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) - static NimBLEServer* m_pServer; -#endif - -#if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER) -# if CONFIG_BT_NIMBLE_EXT_ADV - static NimBLEExtAdvertising* m_bleAdvertising; -# else - static NimBLEAdvertising* m_bleAdvertising; -# endif -#endif - -#if defined( CONFIG_BT_NIMBLE_ROLE_CENTRAL) - static std::list m_cList; -#endif - static std::list m_ignoreList; - static uint32_t m_passkey; - static ble_gap_event_listener m_listener; - static gap_event_handler m_customGapHandler; - static uint8_t m_own_addr_type; -#ifdef ESP_PLATFORM -# ifdef CONFIG_BTDM_BLE_SCAN_DUPL - static uint16_t m_scanDuplicateSize; - static uint8_t m_scanFilterMode; -# endif -#endif - static std::vector m_whiteList; +# endif }; +# if CONFIG_BT_NIMBLE_ROLE_CENTRAL +# include "NimBLEClient.h" +# include "NimBLERemoteService.h" +# include "NimBLERemoteCharacteristic.h" +# include "NimBLERemoteDescriptor.h" +# endif + +# if CONFIG_BT_NIMBLE_ROLE_OBSERVER +# include "NimBLEScan.h" +# endif + +# if CONFIG_BT_NIMBLE_ROLE_PERIPHERAL +# include "NimBLEServer.h" +# include "NimBLEService.h" +# include "NimBLECharacteristic.h" +# include "NimBLEDescriptor.h" +# if CONFIG_BT_NIMBLE_L2CAP_COC_MAX_NUM +# include "NimBLEL2CAPServer.h" +# include "NimBLEL2CAPChannel.h" +# endif +# endif + +# if CONFIG_BT_NIMBLE_ROLE_BROADCASTER +# if CONFIG_BT_NIMBLE_EXT_ADV +# include "NimBLEExtAdvertising.h" +# else +# include "NimBLEAdvertising.h" +# endif +# endif + +# if CONFIG_BT_NIMBLE_ROLE_CENTRAL || CONFIG_BT_NIMBLE_ROLE_PERIPHERAL +# include "NimBLEConnInfo.h" +# endif + +# include "NimBLEAddress.h" +# include "NimBLEUtils.h" + +/** + * @brief Callbacks associated with a BLE device. + */ +class NimBLEDeviceCallbacks { + public: + virtual ~NimBLEDeviceCallbacks() {}; + + /** + * @brief Indicates an inability to perform a store operation. + * This callback should do one of two things: + * -Address the problem and return 0, indicating that the store operation + * should proceed. + * -Return nonzero to indicate that the store operation should be aborted. + * @param event Describes the store event being reported. + * BLE_STORE_EVENT_FULL; or + * BLE_STORE_EVENT_OVERFLOW + * @return 0 if the store operation should proceed; + * nonzero if the store operation should be aborted. + */ + virtual int onStoreStatus(struct ble_store_status_event* event, void* arg); +}; #endif // CONFIG_BT_ENABLED -#endif // MAIN_NIMBLEDEVICE_H_ +#endif // NIMBLE_CPP_DEVICE_H_ diff --git a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEEddystoneTLM.cpp b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEEddystoneTLM.cpp index 1f48a1609..b374d3ac7 100644 --- a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEEddystoneTLM.cpp +++ b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEEddystoneTLM.cpp @@ -1,54 +1,40 @@ /* - * NimBLEEddystoneTLM.cpp + * Copyright 2020-2025 Ryan Powell and + * esp-nimble-cpp, NimBLE-Arduino contributors. * - * Created: on March 15 2020 - * Author H2zero + * 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 * - * Originally: + * http://www.apache.org/licenses/LICENSE-2.0 * - * BLEEddystoneTLM.cpp - * - * Created on: Mar 12, 2018 - * Author: pcbreflux + * 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 "nimconfig.h" -#if defined(CONFIG_BT_ENABLED) - #include "NimBLEEddystoneTLM.h" -#include "NimBLELog.h" +#if CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_BROADCASTER -#include -#include +# include "NimBLEUUID.h" +# include "NimBLELog.h" -#define ENDIAN_CHANGE_U16(x) ((((x)&0xFF00)>>8) + (((x)&0xFF)<<8)) -#define ENDIAN_CHANGE_U32(x) ((((x)&0xFF000000)>>24) + (((x)&0x00FF0000)>>8)) + ((((x)&0xFF00)<<8) + (((x)&0xFF)<<24)) - -static const char LOG_TAG[] = "NimBLEEddystoneTLM"; - -/** - * @brief Construct a default EddystoneTLM beacon object. - */ -NimBLEEddystoneTLM::NimBLEEddystoneTLM() { - beaconUUID = 0xFEAA; - m_eddystoneData.frameType = EDDYSTONE_TLM_FRAME_TYPE; - m_eddystoneData.version = 0; - m_eddystoneData.volt = 3300; // 3300mV = 3.3V - m_eddystoneData.temp = (uint16_t) ((float) 23.00 * 256); // 8.8 fixed format - m_eddystoneData.advCount = 0; - m_eddystoneData.tmil = 0; -} // NimBLEEddystoneTLM +# define ENDIAN_CHANGE_U16(x) ((((x) & 0xFF00) >> 8) + (((x) & 0xFF) << 8)) +# define ENDIAN_CHANGE_U32(x) \ + ((((x) & 0xFF000000) >> 24) + (((x) & 0x00FF0000) >> 8)) + ((((x) & 0xFF00) << 8) + (((x) & 0xFF) << 24)) +static const char* LOG_TAG = "NimBLEEddystoneTLM"; /** * @brief Retrieve the data that is being advertised. * @return The advertised data. */ -std::string NimBLEEddystoneTLM::getData() { - return std::string((char*) &m_eddystoneData, sizeof(m_eddystoneData)); +const NimBLEEddystoneTLM::BeaconData NimBLEEddystoneTLM::getData() { + return m_eddystoneData; } // getData - /** * @brief Get the UUID being advertised. * @return The UUID advertised. @@ -57,7 +43,6 @@ NimBLEUUID NimBLEEddystoneTLM::getUUID() { return NimBLEUUID(beaconUUID); } // getUUID - /** * @brief Get the version being advertised. * @return The version number. @@ -66,7 +51,6 @@ uint8_t NimBLEEddystoneTLM::getVersion() { return m_eddystoneData.version; } // getVersion - /** * @brief Get the battery voltage. * @return The battery voltage. @@ -75,13 +59,12 @@ uint16_t NimBLEEddystoneTLM::getVolt() { return ENDIAN_CHANGE_U16(m_eddystoneData.volt); } // getVolt - /** * @brief Get the temperature being advertised. * @return The temperature value. */ -float NimBLEEddystoneTLM::getTemp() { - return (int16_t)ENDIAN_CHANGE_U16(m_eddystoneData.temp) / 256.0f; +int16_t NimBLEEddystoneTLM::getTemp() { + return ENDIAN_CHANGE_U16(m_eddystoneData.temp); } // getTemp /** @@ -92,7 +75,6 @@ uint32_t NimBLEEddystoneTLM::getCount() { return ENDIAN_CHANGE_U32(m_eddystoneData.advCount); } // getCount - /** * @brief Get the advertisement time. * @return The advertisement time. @@ -101,85 +83,98 @@ uint32_t NimBLEEddystoneTLM::getTime() { return (ENDIAN_CHANGE_U32(m_eddystoneData.tmil)) / 10; } // getTime - /** * @brief Get a string representation of the beacon. * @return The string representation. */ std::string NimBLEEddystoneTLM::toString() { - std::string out = ""; - uint32_t rawsec = ENDIAN_CHANGE_U32(m_eddystoneData.tmil); - char val[12]; + std::string out = ""; + uint32_t rawsec = ENDIAN_CHANGE_U32(m_eddystoneData.tmil); + char val[12]; - out += "Version "; // + std::string(m_eddystoneData.version); - snprintf(val, sizeof(val), "%d", m_eddystoneData.version); - out += val; - out += "\n"; - out += "Battery Voltage "; // + ENDIAN_CHANGE_U16(m_eddystoneData.volt); - snprintf(val, sizeof(val), "%d", ENDIAN_CHANGE_U16(m_eddystoneData.volt)); - out += val; - out += " mV\n"; + out += "Version "; + snprintf(val, sizeof(val), "%d", m_eddystoneData.version); + out += val; + out += "\n"; + out += "Battery Voltage "; + snprintf(val, sizeof(val), "%d", ENDIAN_CHANGE_U16(m_eddystoneData.volt)); + out += val; + out += " mV\n"; - out += "Temperature "; - snprintf(val, sizeof(val), "%.2f", ENDIAN_CHANGE_U16(m_eddystoneData.temp) / 256.0f); - out += val; - out += " C\n"; + out += "Temperature "; + uint8_t intTemp = m_eddystoneData.temp / 256; + uint8_t frac = m_eddystoneData.temp % 256 * 100 / 256; + snprintf(val, sizeof(val), "%d.%d", intTemp, frac); + out += val; + out += " C\n"; - out += "Adv. Count "; - snprintf(val, sizeof(val), "%" PRIu32, ENDIAN_CHANGE_U32(m_eddystoneData.advCount)); - out += val; - out += "\n"; + out += "Adv. Count "; + snprintf(val, sizeof(val), "%" PRIu32, ENDIAN_CHANGE_U32(m_eddystoneData.advCount)); + out += val; + out += "\n"; - out += "Time in seconds "; - snprintf(val, sizeof(val), "%" PRIu32, rawsec/10); - out += val; - out += "\n"; + out += "Time in seconds "; + snprintf(val, sizeof(val), "%" PRIu32, rawsec / 10); + out += val; + out += "\n"; - out += "Time "; + out += "Time "; - snprintf(val, sizeof(val), "%04" PRIu32, rawsec / 864000); - out += val; - out += "."; + snprintf(val, sizeof(val), "%04" PRIu32, rawsec / 864000); + out += val; + out += "."; - snprintf(val, sizeof(val), "%02" PRIu32, (rawsec / 36000) % 24); - out += val; - out += ":"; + snprintf(val, sizeof(val), "%02" PRIu32, (rawsec / 36000) % 24); + out += val; + out += ":"; - snprintf(val, sizeof(val), "%02" PRIu32, (rawsec / 600) % 60); - out += val; - out += ":"; + snprintf(val, sizeof(val), "%02" PRIu32, (rawsec / 600) % 60); + out += val; + out += ":"; - snprintf(val, sizeof(val), "%02" PRIu32, (rawsec / 10) % 60); - out += val; - out += "\n"; + snprintf(val, sizeof(val), "%02" PRIu32, (rawsec / 10) % 60); + out += val; + out += "\n"; - return out; + return out; } // toString +/** + * @brief Set the raw data for the beacon advertisement. + * @param [in] data A pointer to the data to advertise. + * @param [in] length The length of the data. + */ +void NimBLEEddystoneTLM::setData(const uint8_t* data, uint8_t length) { + if (length != sizeof(m_eddystoneData)) { + NIMBLE_LOGE(LOG_TAG, + "Unable to set the data ... length passed in was %d and expected %d", + length, + sizeof(m_eddystoneData)); + return; + } + memcpy(&m_eddystoneData, data, length); +} // setData /** * @brief Set the raw data for the beacon advertisement. * @param [in] data The raw data to advertise. */ -void NimBLEEddystoneTLM::setData(const std::string &data) { - if (data.length() != sizeof(m_eddystoneData)) { - NIMBLE_LOGE(LOG_TAG, "Unable to set the data ... length passed in was %d and expected %d", - data.length(), sizeof(m_eddystoneData)); - return; - } - memcpy(&m_eddystoneData, data.data(), data.length()); +void NimBLEEddystoneTLM::setData(const NimBLEEddystoneTLM::BeaconData& data) { + m_eddystoneData = data; } // setData - /** * @brief Set the UUID to advertise. - * @param [in] l_uuid The UUID. + * @param [in] uuid The UUID. */ -void NimBLEEddystoneTLM::setUUID(const NimBLEUUID &l_uuid) { - beaconUUID = l_uuid.getNative()->u16.value; +void NimBLEEddystoneTLM::setUUID(const NimBLEUUID& uuid) { + if (uuid.bitSize() != 16) { + NIMBLE_LOGE(LOG_TAG, "UUID must be 16 bits"); + return; + } + beaconUUID = *reinterpret_cast(uuid.getValue()); } // setUUID - /** * @brief Set the version to advertise. * @param [in] version The version number. @@ -188,7 +183,6 @@ void NimBLEEddystoneTLM::setVersion(uint8_t version) { m_eddystoneData.version = version; } // setVersion - /** * @brief Set the battery voltage to advertise. * @param [in] volt The voltage in millivolts. @@ -197,16 +191,14 @@ void NimBLEEddystoneTLM::setVolt(uint16_t volt) { m_eddystoneData.volt = volt; } // setVolt - /** * @brief Set the temperature to advertise. - * @param [in] temp The temperature value. + * @param [in] temp The temperature value in 8.8 fixed point format. */ -void NimBLEEddystoneTLM::setTemp(float temp) { - m_eddystoneData.temp = ENDIAN_CHANGE_U16((int16_t)(temp * 256.0f)); +void NimBLEEddystoneTLM::setTemp(int16_t temp) { + m_eddystoneData.temp = temp; } // setTemp - /** * @brief Set the advertisement count. * @param [in] advCount The advertisement number. @@ -215,7 +207,6 @@ void NimBLEEddystoneTLM::setCount(uint32_t advCount) { m_eddystoneData.advCount = advCount; } // setCount - /** * @brief Set the advertisement time. * @param [in] tmil The advertisement time in milliseconds. @@ -224,4 +215,4 @@ void NimBLEEddystoneTLM::setTime(uint32_t tmil) { m_eddystoneData.tmil = tmil; } // setTime -#endif +#endif // CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_BROADCASTER diff --git a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEEddystoneTLM.h b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEEddystoneTLM.h index 265c81b45..2c1e52e39 100644 --- a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEEddystoneTLM.h +++ b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEEddystoneTLM.h @@ -1,25 +1,31 @@ /* - * NimBLEEddystoneTLM.h + * Copyright 2020-2025 Ryan Powell and + * esp-nimble-cpp, NimBLE-Arduino contributors. * - * Created: on March 15 2020 - * Author H2zero + * 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 * - * Originally: + * http://www.apache.org/licenses/LICENSE-2.0 * - * BLEEddystoneTLM.h - * - * Created on: Mar 12, 2018 - * Author: pcbreflux + * 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 _NimBLEEddystoneTLM_H_ -#define _NimBLEEddystoneTLM_H_ +#ifndef NIMBLE_CPP_EDDYSTONETLM_H_ +#define NIMBLE_CPP_EDDYSTONETLM_H_ -#include "NimBLEUUID.h" +#include "nimconfig.h" +#if CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_BROADCASTER -#include +class NimBLEUUID; -#define EDDYSTONE_TLM_FRAME_TYPE 0x20 +# include + +# define EDDYSTONE_TLM_FRAME_TYPE 0x20 /** * @brief Representation of a beacon. @@ -27,35 +33,38 @@ * * https://github.com/google/eddystone */ class NimBLEEddystoneTLM { -public: - NimBLEEddystoneTLM(); - std::string getData(); - NimBLEUUID getUUID(); - uint8_t getVersion(); - uint16_t getVolt(); - float getTemp(); - uint32_t getCount(); - uint32_t getTime(); - std::string toString(); - void setData(const std::string &data); - void setUUID(const NimBLEUUID &l_uuid); - void setVersion(uint8_t version); - void setVolt(uint16_t volt); - void setTemp(float temp); - void setCount(uint32_t advCount); - void setTime(uint32_t tmil); + public: + struct BeaconData { + uint8_t frameType{EDDYSTONE_TLM_FRAME_TYPE}; + uint8_t version{0}; + uint16_t volt{3300}; + uint16_t temp{23 * 256}; + uint32_t advCount{0}; + uint32_t tmil{0}; + } __attribute__((packed)); -private: - uint16_t beaconUUID; - struct { - uint8_t frameType; - uint8_t version; - uint16_t volt; - uint16_t temp; - uint32_t advCount; - uint32_t tmil; - } __attribute__((packed)) m_eddystoneData; + const BeaconData getData(); + NimBLEUUID getUUID(); + uint8_t getVersion(); + uint16_t getVolt(); + int16_t getTemp(); + uint32_t getCount(); + uint32_t getTime(); + std::string toString(); + void setData(const uint8_t* data, uint8_t length); + void setData(const BeaconData& data); + void setUUID(const NimBLEUUID& l_uuid); + void setVersion(uint8_t version); + void setVolt(uint16_t volt); + void setTemp(int16_t temp); + void setCount(uint32_t advCount); + void setTime(uint32_t tmil); + + private: + uint16_t beaconUUID{0xFEAA}; + BeaconData m_eddystoneData; }; // NimBLEEddystoneTLM -#endif /* _NimBLEEddystoneTLM_H_ */ +#endif // CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_BROADCASTER +#endif // NIMBLE_CPP_EDDYSTONETLM_H_ diff --git a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEEddystoneURL.cpp b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEEddystoneURL.cpp deleted file mode 100644 index 73829d79e..000000000 --- a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEEddystoneURL.cpp +++ /dev/null @@ -1,204 +0,0 @@ -/* - * NimBLEEddystoneURL.cpp - * - * Created: on March 15 2020 - * Author H2zero - * - * Originally: - * - * BLEEddystoneURL.cpp - * - * Created on: Mar 12, 2018 - * Author: pcbreflux - */ -#include "nimconfig.h" -#if defined(CONFIG_BT_ENABLED) - -#include "NimBLEEddystoneURL.h" -#include "NimBLELog.h" - -#include - -static const char LOG_TAG[] = "NimBLEEddystoneURL"; - - -/** - * @brief Construct a default EddystoneURL beacon object. - */ -NimBLEEddystoneURL::NimBLEEddystoneURL() { - beaconUUID = 0xFEAA; - lengthURL = 0; - m_eddystoneData.frameType = EDDYSTONE_URL_FRAME_TYPE; - m_eddystoneData.advertisedTxPower = 0; - memset(m_eddystoneData.url, 0, sizeof(m_eddystoneData.url)); -} // BLEEddystoneURL - - -/** - * @brief Retrieve the data that is being advertised. - * @return The advertised data. - */ -std::string NimBLEEddystoneURL::getData() { - return std::string((char*) &m_eddystoneData, sizeof(m_eddystoneData)); -} // getData - - -/** - * @brief Get the UUID being advertised. - * @return The UUID advertised. - */ -NimBLEUUID NimBLEEddystoneURL::getUUID() { - return NimBLEUUID(beaconUUID); -} // getUUID - - -/** - * @brief Get the transmit power being advertised. - * @return The transmit power. - */ -int8_t NimBLEEddystoneURL::getPower() { - return m_eddystoneData.advertisedTxPower; -} // getPower - - -/** - * @brief Get the raw URL being advertised. - * @return The raw URL. - */ -std::string NimBLEEddystoneURL::getURL() { - return std::string((char*) &m_eddystoneData.url, sizeof(m_eddystoneData.url)); -} // getURL - - -/** - * @brief Get the full URL being advertised. - * @return The full URL. - */ -std::string NimBLEEddystoneURL::getDecodedURL() { - std::string decodedURL = ""; - - switch (m_eddystoneData.url[0]) { - case 0x00: - decodedURL += "http://www."; - break; - case 0x01: - decodedURL += "https://www."; - break; - case 0x02: - decodedURL += "http://"; - break; - case 0x03: - decodedURL += "https://"; - break; - default: - decodedURL += m_eddystoneData.url[0]; - } - - for (int i = 1; i < lengthURL; i++) { - if (m_eddystoneData.url[i] > 33 && m_eddystoneData.url[i] < 127) { - decodedURL += m_eddystoneData.url[i]; - } else { - switch (m_eddystoneData.url[i]) { - case 0x00: - decodedURL += ".com/"; - break; - case 0x01: - decodedURL += ".org/"; - break; - case 0x02: - decodedURL += ".edu/"; - break; - case 0x03: - decodedURL += ".net/"; - break; - case 0x04: - decodedURL += ".info/"; - break; - case 0x05: - decodedURL += ".biz/"; - break; - case 0x06: - decodedURL += ".gov/"; - break; - case 0x07: - decodedURL += ".com"; - break; - case 0x08: - decodedURL += ".org"; - break; - case 0x09: - decodedURL += ".edu"; - break; - case 0x0A: - decodedURL += ".net"; - break; - case 0x0B: - decodedURL += ".info"; - break; - case 0x0C: - decodedURL += ".biz"; - break; - case 0x0D: - decodedURL += ".gov"; - break; - default: - break; - } - } - } - return decodedURL; -} // getDecodedURL - - - -/** - * @brief Set the raw data for the beacon advertisement. - * @param [in] data The raw data to advertise. - */ -void NimBLEEddystoneURL::setData(const std::string &data) { - if (data.length() > sizeof(m_eddystoneData)) { - NIMBLE_LOGE(LOG_TAG, "Unable to set the data ... length passed in was %d and max expected %d", - data.length(), sizeof(m_eddystoneData)); - return; - } - memset(&m_eddystoneData, 0, sizeof(m_eddystoneData)); - memcpy(&m_eddystoneData, data.data(), data.length()); - lengthURL = data.length() - (sizeof(m_eddystoneData) - sizeof(m_eddystoneData.url)); -} // setData - - -/** - * @brief Set the UUID to advertise. - * @param [in] l_uuid The UUID. - */ -void NimBLEEddystoneURL::setUUID(const NimBLEUUID &l_uuid) { - beaconUUID = l_uuid.getNative()->u16.value; -} // setUUID - - -/** - * @brief Set the transmit power to advertise. - * @param [in] advertisedTxPower The transmit power level. - */ -void NimBLEEddystoneURL::setPower(int8_t advertisedTxPower) { - m_eddystoneData.advertisedTxPower = advertisedTxPower; -} // setPower - - -/** - * @brief Set the URL to advertise. - * @param [in] url The URL. - */ -void NimBLEEddystoneURL::setURL(const std::string &url) { - if (url.length() > sizeof(m_eddystoneData.url)) { - NIMBLE_LOGE(LOG_TAG, "Unable to set the url ... length passed in was %d and max expected %d", - url.length(), sizeof(m_eddystoneData.url)); - return; - } - memset(m_eddystoneData.url, 0, sizeof(m_eddystoneData.url)); - memcpy(m_eddystoneData.url, url.data(), url.length()); - lengthURL = url.length(); -} // setURL - - -#endif diff --git a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEEddystoneURL.h b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEEddystoneURL.h deleted file mode 100644 index 9c5f37f80..000000000 --- a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEEddystoneURL.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * NimBLEEddystoneURL.h - * - * Created: on March 15 2020 - * Author H2zero - * - * Originally: - * - * BLEEddystoneURL.h - * - * Created on: Mar 12, 2018 - * Author: pcbreflux - */ - -#ifndef _NIMBLEEddystoneURL_H_ -#define _NIMBLEEddystoneURL_H_ -#include "NimBLEUUID.h" - -#include - -#define EDDYSTONE_URL_FRAME_TYPE 0x10 - -/** - * @brief Representation of a beacon. - * See: - * * https://github.com/google/eddystone - */ -class NimBLEEddystoneURL { -public: - NimBLEEddystoneURL(); - std::string getData(); - NimBLEUUID getUUID(); - int8_t getPower(); - std::string getURL(); - std::string getDecodedURL(); - void setData(const std::string &data); - void setUUID(const NimBLEUUID &l_uuid); - void setPower(int8_t advertisedTxPower); - void setURL(const std::string &url); - -private: - uint16_t beaconUUID; - uint8_t lengthURL; - struct { - uint8_t frameType; - int8_t advertisedTxPower; - uint8_t url[16]; - } __attribute__((packed)) m_eddystoneData; - -}; // NIMBLEEddystoneURL - -#endif /* _NIMBLEEddystoneURL_H_ */ diff --git a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEExtAdvertising.cpp b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEExtAdvertising.cpp index 67d8a586a..d3a020610 100644 --- a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEExtAdvertising.cpp +++ b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEExtAdvertising.cpp @@ -1,48 +1,62 @@ /* - * NimBLEExtAdvertising.cpp + * Copyright 2020-2025 Ryan Powell and + * esp-nimble-cpp, NimBLE-Arduino contributors. * - * Created: on February 6, 2022 - * Author H2zero + * 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 "nimconfig.h" -#if defined(CONFIG_BT_ENABLED) && \ - defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER) && \ - CONFIG_BT_NIMBLE_EXT_ADV - -#if defined(CONFIG_NIMBLE_CPP_IDF) -#include "services/gap/ble_svc_gap.h" -#else -#include "nimble/nimble/host/services/gap/include/services/gap/ble_svc_gap.h" -#endif #include "NimBLEExtAdvertising.h" -#include "NimBLEDevice.h" -#include "NimBLEServer.h" -#include "NimBLEUtils.h" -#include "NimBLELog.h" +#if CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_BROADCASTER && CONFIG_BT_NIMBLE_EXT_ADV + +# if defined(CONFIG_NIMBLE_CPP_IDF) +# include "services/gap/ble_svc_gap.h" +# else +# include "nimble/nimble/host/services/gap/include/services/gap/ble_svc_gap.h" +# endif + +# include "NimBLEDevice.h" +# include "NimBLEServer.h" +# include "NimBLEUtils.h" +# include "NimBLELog.h" static NimBLEExtAdvertisingCallbacks defaultCallbacks; -static const char* LOG_TAG = "NimBLEExtAdvertising"; +static const char* LOG_TAG = "NimBLEExtAdvertising"; +/** + * @brief Constructor. + */ +NimBLEExtAdvertising::NimBLEExtAdvertising() + : m_deleteCallbacks{false}, + m_pCallbacks{&defaultCallbacks}, + m_advStatus(CONFIG_BT_NIMBLE_MAX_EXT_ADV_INSTANCES + 1, false) {} /** * @brief Destructor: deletes callback instances if requested. */ NimBLEExtAdvertising::~NimBLEExtAdvertising() { - if(m_deleteCallbacks && m_pCallbacks != &defaultCallbacks) { + if (m_deleteCallbacks) { delete m_pCallbacks; } } - /** * @brief Register the extended advertisement data. - * @param [in] inst_id The extended advertisement instance ID to assign to this data. + * @param [in] instId The extended advertisement instance ID to assign to this data. * @param [in] adv The extended advertisement instance with the data to set. * @return True if advertising started successfully. */ -bool NimBLEExtAdvertising::setInstanceData(uint8_t inst_id, NimBLEExtAdvertisement& adv) { - adv.m_params.sid = inst_id; +bool NimBLEExtAdvertising::setInstanceData(uint8_t instId, NimBLEExtAdvertisement& adv) { + adv.m_params.sid = instId; // Legacy advertising as connectable requires the scannable flag also. if (adv.m_params.legacy_pdu && adv.m_params.connectable) { @@ -54,165 +68,127 @@ bool NimBLEExtAdvertising::setInstanceData(uint8_t inst_id, NimBLEExtAdvertiseme adv.m_params.scan_req_notif = false; } -#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) +# if CONFIG_BT_NIMBLE_ROLE_PERIPHERAL NimBLEServer* pServer = NimBLEDevice::getServer(); if (pServer != nullptr) { - if (!pServer->m_gattsStarted) { - pServer->start(); - } + pServer->start(); // make sure the GATT server is ready before advertising } - int rc = ble_gap_ext_adv_configure(inst_id, - &adv.m_params, - NULL, - (pServer != nullptr) ? NimBLEServer::handleGapEvent : - NimBLEExtAdvertising::handleGapEvent, - NULL); -#else - int rc = ble_gap_ext_adv_configure(inst_id, - &data.m_params, - NULL, - NimBLEExtAdvertising::handleGapEvent, - NULL); -#endif + int rc = ble_gap_ext_adv_configure( + instId, + &adv.m_params, + NULL, + (pServer != nullptr) ? NimBLEServer::handleGapEvent : NimBLEExtAdvertising::handleGapEvent, + NULL); +# else + int rc = ble_gap_ext_adv_configure(instId, &adv.m_params, NULL, NimBLEExtAdvertising::handleGapEvent, NULL); +# endif if (rc != 0) { - NIMBLE_LOGE(LOG_TAG, "Advertising config error: rc = %d", rc); - } else { - os_mbuf *buf; - buf = os_msys_get_pkthdr(adv.m_payload.size(), 0); - if (!buf) { - NIMBLE_LOGE(LOG_TAG, "Data buffer allocation failed"); - return false; - } - - rc = os_mbuf_append(buf, &adv.m_payload[0], adv.m_payload.size()); - if (rc != 0) { - NIMBLE_LOGE(LOG_TAG, "Unable to copy data: rc = %d", rc); - return false; - } else { - if (adv.m_params.scannable && !adv.m_params.legacy_pdu) { - rc = ble_gap_ext_adv_rsp_set_data(inst_id, buf); - } else { - rc = ble_gap_ext_adv_set_data(inst_id, buf); - } - - if (rc != 0) { - NIMBLE_LOGE(LOG_TAG, "Invalid advertisement data: rc = %d", rc); - } else { - if (adv.m_advAddress != NimBLEAddress("")) { - ble_addr_t addr; - memcpy(&addr.val, adv.m_advAddress.getNative(), 6); - // Custom advertising address must be random. - addr.type = BLE_OWN_ADDR_RANDOM; - rc = ble_gap_ext_adv_set_addr(inst_id, &addr); - } - - if (rc != 0) { - NIMBLE_LOGE(LOG_TAG, "Error setting advertisement address: rc = %d", rc); - return false; - } - } - } + NIMBLE_LOGE(LOG_TAG, "Advertising config error: rc = %d %s", rc, NimBLEUtils::returnCodeToString(rc)); + return false; } - return (rc == 0); -} - - -/** - * @brief Set the scan response data for a legacy advertisement. - * @param [in] inst_id The extended advertisement instance ID to assign to this data. - * @param [in] lsr A reference to a NimBLEExtAdvertisement that contains the data. - */ -bool NimBLEExtAdvertising::setScanResponseData(uint8_t inst_id, NimBLEExtAdvertisement & lsr) { - os_mbuf *buf = os_msys_get_pkthdr(lsr.m_payload.size(), 0); + os_mbuf* buf; + buf = os_msys_get_pkthdr(adv.m_payload.size(), 0); if (!buf) { NIMBLE_LOGE(LOG_TAG, "Data buffer allocation failed"); return false; } - int rc = os_mbuf_append(buf, &lsr.m_payload[0], lsr.m_payload.size()); + rc = os_mbuf_append(buf, &adv.m_payload[0], adv.m_payload.size()); + if (rc != 0) { + NIMBLE_LOGE(LOG_TAG, "Unable to copy data: rc = %d %s", rc, NimBLEUtils::returnCodeToString(rc)); + return false; + } + + if (adv.m_params.scannable && !adv.m_params.legacy_pdu) { + rc = ble_gap_ext_adv_rsp_set_data(instId, buf); + } else { + rc = ble_gap_ext_adv_set_data(instId, buf); + } if (rc != 0) { - NIMBLE_LOGE(LOG_TAG, "Unable to copy scan data: rc = %d", rc); + NIMBLE_LOGE(LOG_TAG, "Invalid advertisement data: rc = %d %s", rc, NimBLEUtils::returnCodeToString(rc)); return false; - } else { - rc = ble_gap_ext_adv_rsp_set_data(inst_id, buf); } - return (rc == 0); -} + if (!adv.m_advAddress.isNull()) { + rc = ble_gap_ext_adv_set_addr(instId, adv.m_advAddress.getBase()); + } + + if (rc != 0) { + NIMBLE_LOGE(LOG_TAG, "Error setting advertisement address: rc = %d %s", rc, NimBLEUtils::returnCodeToString(rc)); + return false; + } + + return true; +} // setInstanceData + +/** + * @brief Set the scan response data for a legacy advertisement. + * @param [in] instId The extended advertisement instance ID to assign to this data. + * @param [in] data A reference to a NimBLEExtAdvertisement that contains the data. + */ +bool NimBLEExtAdvertising::setScanResponseData(uint8_t instId, NimBLEExtAdvertisement& data) { + os_mbuf* buf = os_msys_get_pkthdr(data.m_payload.size(), 0); + if (!buf) { + NIMBLE_LOGE(LOG_TAG, "Data buffer allocation failed"); + return false; + } + + int rc = os_mbuf_append(buf, &data.m_payload[0], data.m_payload.size()); + if (rc != 0) { + NIMBLE_LOGE(LOG_TAG, "Unable to copy scan data: rc = %d %s", rc, NimBLEUtils::returnCodeToString(rc)); + return false; + } + + rc = ble_gap_ext_adv_rsp_set_data(instId, buf); + return (rc == 0); +} // setScanResponseData /** * @brief Start extended advertising. - * @param [in] inst_id The extended advertisement instance ID to start. + * @param [in] instId The extended advertisement instance ID to start. * @param [in] duration How long to advertise for in milliseconds, 0 = forever (default). - * @param [in] max_events Maximum number of advertisement events to send, 0 = no limit (default). + * @param [in] maxEvents Maximum number of advertisement events to send, 0 = no limit (default). * @return True if advertising started successfully. */ -bool NimBLEExtAdvertising::start(uint8_t inst_id, int duration, int max_events) { - NIMBLE_LOGD(LOG_TAG, ">> Extended Advertising start"); - +bool NimBLEExtAdvertising::start(uint8_t instId, int duration, int maxEvents) { // If Host is not synced we cannot start advertising. - if(!NimBLEDevice::m_synced) { + if (!NimBLEDevice::m_synced) { NIMBLE_LOGE(LOG_TAG, "Host reset, wait for sync."); return false; } - int rc = ble_gap_ext_adv_start(inst_id, duration / 10, max_events); - - switch (rc) { - case 0: - m_advStatus[inst_id] = true; - break; - - case BLE_HS_EINVAL: - NIMBLE_LOGE(LOG_TAG, "Unable to advertise - Value Error"); - break; - - case BLE_HS_EALREADY: - NIMBLE_LOGI(LOG_TAG, "Advertisement Already active"); - break; - - case BLE_HS_ETIMEOUT_HCI: - case BLE_HS_EOS: - case BLE_HS_ECONTROLLER: - case BLE_HS_ENOTSYNCED: - NIMBLE_LOGE(LOG_TAG, "Unable to advertise - Host Reset"); - break; - - default: - NIMBLE_LOGE(LOG_TAG, "Error enabling advertising; rc=%d, %s", - rc, NimBLEUtils::returnCodeToString(rc)); - break; + int rc = ble_gap_ext_adv_start(instId, duration / 10, maxEvents); + if (rc != 0 && rc != BLE_HS_EALREADY) { + NIMBLE_LOGE(LOG_TAG, "Error enabling advertising; rc=%d, %s", rc, NimBLEUtils::returnCodeToString(rc)); + return false; } - NIMBLE_LOGD(LOG_TAG, "<< Extended Advertising start"); - return (rc == 0 || rc == BLE_HS_EALREADY); + m_advStatus[instId] = true; + return true; } // start - /** * @brief Stop and remove this instance data from the advertisement set. - * @param [in] inst_id The extended advertisement instance to stop advertising. + * @param [in] instId The extended advertisement instance to stop advertising. * @return True if successful. */ -bool NimBLEExtAdvertising::removeInstance(uint8_t inst_id) { - if (stop(inst_id)) { - int rc = ble_gap_ext_adv_remove(inst_id); - if (rc != 0 && rc != BLE_HS_EALREADY) { - NIMBLE_LOGE(LOG_TAG, "ble_gap_ext_adv_remove rc = %d %s", - rc, NimBLEUtils::returnCodeToString(rc)); - return false; +bool NimBLEExtAdvertising::removeInstance(uint8_t instId) { + if (stop(instId)) { + int rc = ble_gap_ext_adv_remove(instId); + if (rc == 0 || rc == BLE_HS_EALREADY) { + return true; } - return true; + + NIMBLE_LOGE(LOG_TAG, "ble_gap_ext_adv_remove rc = %d %s", rc, NimBLEUtils::returnCodeToString(rc)); } return false; } // removeInstance - /** * @brief Stop and remove all advertising instance data. * @return True if successful. @@ -222,113 +198,102 @@ bool NimBLEExtAdvertising::removeAll() { int rc = ble_gap_ext_adv_clear(); if (rc == 0 || rc == BLE_HS_EALREADY) { return true; - } else { - NIMBLE_LOGE(LOG_TAG, "ble_gap_ext_adv_clear rc = %d %s", - rc, NimBLEUtils::returnCodeToString(rc)); } + + NIMBLE_LOGE(LOG_TAG, "ble_gap_ext_adv_clear rc = %d %s", rc, NimBLEUtils::returnCodeToString(rc)); } return false; } // removeAll - /** * @brief Stop advertising this instance data. - * @param [in] inst_id The extended advertisement instance to stop advertising. + * @param [in] instId The extended advertisement instance to stop advertising. * @return True if successful. */ -bool NimBLEExtAdvertising::stop(uint8_t inst_id) { - int rc = ble_gap_ext_adv_stop(inst_id); - if (rc != 0 && rc != BLE_HS_EALREADY) { - NIMBLE_LOGE(LOG_TAG, "ble_gap_ext_adv_stop rc = %d %s", - rc, NimBLEUtils::returnCodeToString(rc)); - return false; +bool NimBLEExtAdvertising::stop(uint8_t instId) { + int rc = ble_gap_ext_adv_stop(instId); + if (rc == 0 || rc == BLE_HS_EALREADY) { + m_advStatus[instId] = false; + return true; } - m_advStatus[inst_id] = false; - return true; + NIMBLE_LOGE(LOG_TAG, "ble_gap_ext_adv_stop rc = %d %s", rc, NimBLEUtils::returnCodeToString(rc)); + return false; } // stop - /** * @brief Stop all advertisements. * @return True if successful. */ bool NimBLEExtAdvertising::stop() { int rc = ble_gap_ext_adv_clear(); - if (rc != 0 && rc != BLE_HS_EALREADY) { - NIMBLE_LOGE(LOG_TAG, "ble_gap_ext_adv_stop rc = %d %s", - rc, NimBLEUtils::returnCodeToString(rc)); - return false; + if (rc == 0 || rc == BLE_HS_EALREADY) { + for (auto status : m_advStatus) { + status = false; + } + return true; } - for(auto it : m_advStatus) { - it = false; - } - - return true; + NIMBLE_LOGE(LOG_TAG, "ble_gap_ext_adv_stop rc = %d %s", rc, NimBLEUtils::returnCodeToString(rc)); + return false; } // stop - /** * @brief Set a callback to call when the advertisement stops. * @param [in] pCallbacks A pointer to a callback to be invoked when an advertisement stops. * @param [in] deleteCallbacks if true callback class will be deleted when advertising is destructed. */ -void NimBLEExtAdvertising::setCallbacks(NimBLEExtAdvertisingCallbacks* pCallbacks, - bool deleteCallbacks) { - if (pCallbacks != nullptr){ - m_pCallbacks = pCallbacks; +void NimBLEExtAdvertising::setCallbacks(NimBLEExtAdvertisingCallbacks* pCallbacks, bool deleteCallbacks) { + if (pCallbacks != nullptr) { + m_pCallbacks = pCallbacks; m_deleteCallbacks = deleteCallbacks; } else { - m_pCallbacks = &defaultCallbacks; + m_pCallbacks = &defaultCallbacks; + m_deleteCallbacks = false; } } // setCallbacks - /** * @brief Check if currently advertising. - * @param [in] inst_id The instance ID of the advertised data to get the status of. + * @param [in] instId The instance ID of the advertised data to get the status of. * @return True if advertising is active. */ -bool NimBLEExtAdvertising::isActive(uint8_t inst_id) { - return m_advStatus[inst_id]; +bool NimBLEExtAdvertising::isActive(uint8_t instId) { + return m_advStatus[instId]; } // isAdvertising - /** * @brief Check if any instances are currently advertising. * @return True if any instance is active. */ bool NimBLEExtAdvertising::isAdvertising() { - for (auto it : m_advStatus) { - if (it) { + for (const auto status : m_advStatus) { + if (status) { return true; } } + return false; } // isAdvertising - /* * Host reset seems to clear advertising data, * we need clear the flag so it reloads it. */ void NimBLEExtAdvertising::onHostSync() { NIMBLE_LOGD(LOG_TAG, "Host re-synced"); - for(auto it : m_advStatus) { - it = false; + for (auto status : m_advStatus) { + status = false; } } // onHostSync - /** * @brief Handler for gap events when not using peripheral role. * @param [in] event the event data. * @param [in] arg pointer to the advertising instance. */ -/*STATIC*/ -int NimBLEExtAdvertising::handleGapEvent(struct ble_gap_event *event, void *arg) { +int NimBLEExtAdvertising::handleGapEvent(ble_gap_event* event, void* arg) { (void)arg; NimBLEExtAdvertising* pAdv = NimBLEDevice::getAdvertising(); @@ -348,34 +313,36 @@ int NimBLEExtAdvertising::handleGapEvent(struct ble_gap_event *event, void *arg) break; } pAdv->m_advStatus[event->adv_complete.instance] = false; - pAdv->m_pCallbacks->onStopped(pAdv, event->adv_complete.reason, - event->adv_complete.instance); + pAdv->m_pCallbacks->onStopped(pAdv, event->adv_complete.reason, event->adv_complete.instance); break; - } + } // BLE_GAP_EVENT_ADV_COMPLETE case BLE_GAP_EVENT_SCAN_REQ_RCVD: { - pAdv->m_pCallbacks->onScanRequest(pAdv, event->scan_req_rcvd.instance, + pAdv->m_pCallbacks->onScanRequest(pAdv, + event->scan_req_rcvd.instance, NimBLEAddress(event->scan_req_rcvd.scan_addr)); break; - } + } // BLE_GAP_EVENT_SCAN_REQ_RCVD } return 0; } // handleGapEvent +/* -------------------------------------------------------------------------- */ +/* Default callback handlers */ +/* -------------------------------------------------------------------------- */ -/** Default callback handlers */ -void NimBLEExtAdvertisingCallbacks::onStopped(NimBLEExtAdvertising *pAdv, - int reason, uint8_t inst_id) { +void NimBLEExtAdvertisingCallbacks::onStopped(NimBLEExtAdvertising* pAdv, int reason, uint8_t instId) { NIMBLE_LOGD("NimBLEExtAdvertisingCallbacks", "onStopped: Default"); } // onStopped - -void NimBLEExtAdvertisingCallbacks::onScanRequest(NimBLEExtAdvertising *pAdv, - uint8_t inst_id, NimBLEAddress addr) { +void NimBLEExtAdvertisingCallbacks::onScanRequest(NimBLEExtAdvertising* pAdv, uint8_t instId, NimBLEAddress addr) { NIMBLE_LOGD("NimBLEExtAdvertisingCallbacks", "onScanRequest: Default"); } // onScanRequest +/* -------------------------------------------------------------------------- */ +/* Advertisement Data */ +/* -------------------------------------------------------------------------- */ /** * @brief Construct a BLE extended advertisement. @@ -387,68 +354,57 @@ void NimBLEExtAdvertisingCallbacks::onScanRequest(NimBLEExtAdvertising *pAdv, * * BLE_HCI_LE_PHY_2M * * BLE_HCI_LE_PHY_CODED */ -NimBLEExtAdvertisement::NimBLEExtAdvertisement(uint8_t priPhy, uint8_t secPhy) -: m_advAddress("") -{ - memset (&m_params, 0, sizeof(m_params)); - m_params.own_addr_type = NimBLEDevice::m_own_addr_type; +NimBLEExtAdvertisement::NimBLEExtAdvertisement(uint8_t priPhy, uint8_t secPhy) { + m_params.own_addr_type = NimBLEDevice::m_ownAddrType; m_params.primary_phy = priPhy; m_params.secondary_phy = secPhy; - m_params.tx_power = 127; + m_params.tx_power = NimBLEDevice::getPower(NimBLETxPowerType::Advertise); } // NimBLEExtAdvertisement - /** * @brief Sets wether the advertisement should use legacy (BLE 4.0, 31 bytes max) advertising. - * @param [in] val true = using legacy advertising. + * @param [in] enable true = using legacy advertising. */ -void NimBLEExtAdvertisement::setLegacyAdvertising(bool val) { - m_params.legacy_pdu = val; +void NimBLEExtAdvertisement::setLegacyAdvertising(bool enable) { + m_params.legacy_pdu = enable; } // setLegacyAdvertising - /** * @brief Sets wether the advertisement has scan response data available. - * @param [in] val true = scan response is available. + * @param [in] enable true = scan response is available. */ -void NimBLEExtAdvertisement::setScannable(bool val) { - m_params.scannable = val; +void NimBLEExtAdvertisement::setScannable(bool enable) { + m_params.scannable = enable; } // setScannable - - /** * @brief Sets the transmission power level for this advertisement. * @param [in] dbm the transmission power to use in dbm. - * @details The allowable value range depends on device hardware. \n - * The ESP32C3 and ESP32S3 have a range of -27 to +18. + * @details The allowable value range depends on device hardware. */ void NimBLEExtAdvertisement::setTxPower(int8_t dbm) { m_params.tx_power = dbm; -} - +} // setTxPower /** * @brief Sets wether this advertisement should advertise as a connectable device. - * @param [in] val True = connectable. + * @param [in] enable True = connectable. */ -void NimBLEExtAdvertisement::setConnectable(bool val) { -#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) - m_params.connectable = val; -#endif +void NimBLEExtAdvertisement::setConnectable(bool enable) { +# if CONFIG_BT_NIMBLE_ROLE_PERIPHERAL + m_params.connectable = enable; +# endif } // setConnectable - /** * @brief Set the address to use for this advertisement. * @param [in] addr The address to use. */ -void NimBLEExtAdvertisement::setAddress(const NimBLEAddress & addr) { - m_advAddress = addr; +void NimBLEExtAdvertisement::setAddress(const NimBLEAddress& addr) { + m_advAddress = addr; // Must use random address type. m_params.own_addr_type = BLE_OWN_ADDR_RANDOM; -} - +} // setAddress /** * @brief Sets The primary channels to advertise on. @@ -462,7 +418,6 @@ void NimBLEExtAdvertisement::setPrimaryChannels(bool ch37, bool ch38, bool ch39) m_params.channel_map = (ch37 | (ch38 << 1) | (ch39 << 2)); } // setPrimaryChannels - /** * @brief Set the filtering for the scan filter. * @param [in] scanRequestWhitelistOnly If true, only allow scan requests from those on the white list. @@ -487,30 +442,24 @@ void NimBLEExtAdvertisement::setScanFilter(bool scanRequestWhitelistOnly, bool c } } // setScanFilter - /** * @brief Sets the peer to directly advertise to. * @param [in] addr The address of the peer to direct the advertisements. */ -void NimBLEExtAdvertisement::setDirectedPeer(const NimBLEAddress & addr) { - ble_addr_t peerAddr; - memcpy(&peerAddr.val, addr.getNative(), 6); - peerAddr.type = addr.getType(); - m_params.peer = peerAddr; +void NimBLEExtAdvertisement::setDirectedPeer(const NimBLEAddress& addr) { + m_params.peer = *addr.getBase(); } // setDirectedPeer - /** * @brief Enable or disable direct advertisements to the peer set with `NimBLEExtAdvertisement::setDirectedPeer` - * @param [in] val true = send directed advertisements to peer. + * @param [in] enable true = send directed advertisements to peer. * @param [in] high_duty true = use fast advertising rate, default - true. */ -void NimBLEExtAdvertisement::setDirected(bool val, bool high_duty) { - m_params.directed = val; +void NimBLEExtAdvertisement::setDirected(bool enable, bool high_duty) { + m_params.directed = enable; m_params.high_duty_directed = high_duty; } // setDirected - /** * @brief Set the minimum advertising interval. * @param [in] mininterval Minimum value for advertising interval in 0.625ms units, 0 = use default. @@ -519,7 +468,6 @@ void NimBLEExtAdvertisement::setMinInterval(uint32_t mininterval) { m_params.itvl_min = mininterval; } // setMinInterval - /** * @brief Set the maximum advertising interval. * @param [in] maxinterval Maximum value for advertising interval in 0.625ms units, 0 = use default. @@ -528,7 +476,6 @@ void NimBLEExtAdvertisement::setMaxInterval(uint32_t maxinterval) { m_params.itvl_max = maxinterval; } // setMaxInterval - /** * @brief Set the primary advertising PHY to use * @param [in] phy Can be one of following constants: @@ -539,7 +486,6 @@ void NimBLEExtAdvertisement::setPrimaryPhy(uint8_t phy) { m_params.primary_phy = phy; } // setPrimaryPhy - /** * @brief Set the secondary advertising PHY to use * @param [in] phy Can be one of following constants: @@ -551,18 +497,16 @@ void NimBLEExtAdvertisement::setSecondaryPhy(uint8_t phy) { m_params.secondary_phy = phy; } // setSecondaryPhy - /** * @brief Sets whether the advertisement should be anonymous. - * @param [in] val Set to true to enable anonymous advertising. + * @param [in] enable Set to true to enable anonymous advertising. * * @details Anonymous advertising omits the device's address from the advertisement. */ -void NimBLEExtAdvertisement::setAnonymous(bool val) { - m_params.anonymous = val; +void NimBLEExtAdvertisement::setAnonymous(bool enable) { + m_params.anonymous = enable; } // setAnonymous - /** * @brief Sets whether the scan response request callback should be called. * @param [in] enable If true the scan response request callback will be called for this advertisement. @@ -571,66 +515,77 @@ void NimBLEExtAdvertisement::enableScanRequestCallback(bool enable) { m_params.scan_req_notif = enable; } // enableScanRequestCallback - /** * @brief Clears the data stored in this instance, does not change settings. * @details This will clear all data but preserves advertising parameter settings. */ void NimBLEExtAdvertisement::clearData() { - std::vector swap; - std::swap(m_payload, swap); -} - - -/** - * @brief Get the size of the current data. - */ -size_t NimBLEExtAdvertisement::getDataSize() { - return m_payload.size(); -} // getDataSize - + std::vector().swap(m_payload); +} // clearData /** * @brief Set the advertisement data. * @param [in] data The data to be set as the payload. * @param [in] length The size of data. + * @return True if successful, false if the data is too large. * @details This will completely replace any data that was previously set. */ -void NimBLEExtAdvertisement::setData(const uint8_t * data, size_t length) { - m_payload.assign(data, data + length); +bool NimBLEExtAdvertisement::setData(const uint8_t* data, size_t length) { + if (length > CONFIG_BT_NIMBLE_MAX_EXT_ADV_DATA_LEN) { + return false; + } + + std::vector(data, data + length).swap(m_payload); + return true; } // setData - -/** - * @brief Add data to the payload to be advertised. - * @param [in] data The data to be added to the payload. - */ -void NimBLEExtAdvertisement::addData(const std::string &data) { - addData((uint8_t*)data.data(), data.length()); -} // addData - - /** * @brief Add data to the payload to be advertised. * @param [in] data The data to be added to the payload. * @param [in] length The size of data to be added to the payload. + * @return True if successful, false if the data is too large. */ -void NimBLEExtAdvertisement::addData(const uint8_t * data, size_t length) { +bool NimBLEExtAdvertisement::addData(const uint8_t* data, size_t length) { + if (m_payload.size() + length > CONFIG_BT_NIMBLE_MAX_EXT_ADV_DATA_LEN) { + 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. + * @return True if successful, false if the data is too large. + */ +bool NimBLEExtAdvertisement::addData(const std::string& data) { + if (m_payload.size() + data.length() > CONFIG_BT_NIMBLE_MAX_EXT_ADV_DATA_LEN) { + return false; + } + + m_payload.insert(m_payload.end(), data.begin(), data.end()); + return true; +} // 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. */ -void NimBLEExtAdvertisement::setAppearance(uint16_t appearance) { - char cdata[2]; - cdata[0] = 3; - cdata[1] = BLE_HS_ADV_TYPE_APPEARANCE; // 0x19 - addData(std::string(cdata, 2) + std::string((char*) &appearance, 2)); -} // setAppearance +bool NimBLEExtAdvertisement::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. @@ -638,230 +593,501 @@ void NimBLEExtAdvertisement::setAppearance(uint16_t appearance) { * * BLE_HS_ADV_F_DISC_LTD * * BLE_HS_ADV_F_DISC_GEN * * BLE_HS_ADV_F_BREDR_UNSUP - must always use with NimBLE + * @return True if successful. + * @details If the flag value is 0 then it will be removed from the advertisement if set previously. */ -void NimBLEExtAdvertisement::setFlags(uint8_t flag) { - char cdata[3]; - cdata[0] = 2; - cdata[1] = BLE_HS_ADV_TYPE_FLAGS; // 0x01 - cdata[2] = flag | BLE_HS_ADV_F_BREDR_UNSUP; - addData(std::string(cdata, 3)); -} // setFlags +bool NimBLEExtAdvertisement::setFlags(uint8_t flag) { + if (flag == 0) { + removeData(BLE_HS_ADV_TYPE_FLAGS); + return true; + } + 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 Set manufacturer specific data. * @param [in] data The manufacturer data to advertise. + * @param [in] length The length of the data. + * @return True if successful. */ -void NimBLEExtAdvertisement::setManufacturerData(const std::string &data) { - char cdata[2]; - cdata[0] = data.length() + 1; - cdata[1] = BLE_HS_ADV_TYPE_MFG_DATA ; // 0xff - addData(std::string(cdata, 2) + data); +bool NimBLEExtAdvertisement::setManufacturerData(const uint8_t* data, size_t length) { + uint8_t header[2]; + header[0] = length + 1; + header[1] = BLE_HS_ADV_TYPE_MFG_DATA; + + if (addData(header, 2)) { + return addData(data, length); + } + + m_payload.erase(m_payload.end() - 2, m_payload.end()); // Remove the header if failed. + return false; } // setManufacturerData +/** + * @brief Set manufacturer specific data. + * @param [in] data The manufacturer data to advertise. + * @return True if successful. + */ +bool NimBLEExtAdvertisement::setManufacturerData(const std::string& data) { + return setManufacturerData(reinterpret_cast(data.data()), data.length()); +} // setManufacturerData + +/** + * @brief Set manufacturer specific data. + * @param [in] data The manufacturer data to advertise. + * @return True if successful. + */ +bool NimBLEExtAdvertisement::setManufacturerData(const std::vector& 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. */ -void NimBLEExtAdvertisement::setURI(const std::string &uri) { - char cdata[2]; - cdata[0] = uri.length() + 1; - cdata[1] = BLE_HS_ADV_TYPE_URI; - addData(std::string(cdata, 2) + uri); -} // setURI +bool NimBLEExtAdvertisement::setURI(const std::string& uri) { + uint8_t header[2]; + header[0] = uri.length() + 1; + header[1] = BLE_HS_ADV_TYPE_URI; + if (addData(header, 2)) { + return addData(reinterpret_cast(uri.data()), uri.length()); + } + m_payload.erase(m_payload.end() - 2, m_payload.end()); // Remove the header if failed. + return false; +} // 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, if false it is shortened. + * @return True if successful. */ -void NimBLEExtAdvertisement::setName(const std::string &name) { - char cdata[2]; - cdata[0] = name.length() + 1; - cdata[1] = BLE_HS_ADV_TYPE_COMP_NAME; // 0x09 - addData(std::string(cdata, 2) + name); +bool NimBLEExtAdvertisement::setName(const std::string& name, bool isComplete) { + uint8_t header[2]; + header[0] = name.length() + 1; + header[1] = isComplete ? BLE_HS_ADV_TYPE_COMP_NAME : BLE_HS_ADV_TYPE_INCOMP_NAME; + + if (addData(header, 2)) { + return addData(reinterpret_cast(name.data()), name.length()); + } + + m_payload.erase(m_payload.end() - 2, m_payload.end()); // Remove the header if failed. + return false; } // setName +/** + * @brief Add a service uuid to exposed list of services. + * @param [in] serviceUUID The UUID of the service to expose. + */ +bool NimBLEExtAdvertisement::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 + getDataSize() > CONFIG_BT_NIMBLE_MAX_EXT_ADV_DATA_LEN) { + 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 NimBLEExtAdvertisement::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 NimBLEExtAdvertisement::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 NimBLEExtAdvertisement::removeServiceUUID(const char* serviceUUID) { + return removeServiceUUID(NimBLEUUID(serviceUUID)); +} // removeServiceUUID + +/** + * @brief Remove all service UUIDs from the advertisement. + */ +bool NimBLEExtAdvertisement::removeServices() { + return true; +} // removeServices /** * @brief Set a single service to advertise as a complete list of services. * @param [in] uuid The service to advertise. + * @return True if successful. */ -void NimBLEExtAdvertisement::setCompleteServices(const NimBLEUUID &uuid) { - setServices(true, uuid.bitSize(), {uuid}); +bool NimBLEExtAdvertisement::setCompleteServices(const NimBLEUUID& uuid) { + return setServices(true, uuid.bitSize(), {uuid}); } // setCompleteServices - /** * @brief Set the complete list of 16 bit services to advertise. * @param [in] v_uuid A vector of 16 bit UUID's to advertise. + * @return True if successful. */ -void NimBLEExtAdvertisement::setCompleteServices16(const std::vector& v_uuid) { - setServices(true, 16, v_uuid); +bool NimBLEExtAdvertisement::setCompleteServices16(const std::vector& v_uuid) { + return setServices(true, 16, v_uuid); } // setCompleteServices16 - /** * @brief Set the complete list of 32 bit services to advertise. * @param [in] v_uuid A vector of 32 bit UUID's to advertise. + * @return True if successful. */ -void NimBLEExtAdvertisement::setCompleteServices32(const std::vector& v_uuid) { - setServices(true, 32, v_uuid); +bool NimBLEExtAdvertisement::setCompleteServices32(const std::vector& v_uuid) { + return setServices(true, 32, v_uuid); } // setCompleteServices32 - /** * @brief Set a single service to advertise as a partial list of services. * @param [in] uuid The service to advertise. + * @return True if successful. */ -void NimBLEExtAdvertisement::setPartialServices(const NimBLEUUID &uuid) { - setServices(false, uuid.bitSize(), {uuid}); +bool NimBLEExtAdvertisement::setPartialServices(const NimBLEUUID& uuid) { + return setServices(false, uuid.bitSize(), {uuid}); } // setPartialServices - /** * @brief Set the partial list of services to advertise. * @param [in] v_uuid A vector of 16 bit UUID's to advertise. + * @return True if successful. */ -void NimBLEExtAdvertisement::setPartialServices16(const std::vector& v_uuid) { - setServices(false, 16, v_uuid); +bool NimBLEExtAdvertisement::setPartialServices16(const std::vector& v_uuid) { + return setServices(false, 16, v_uuid); } // setPartialServices16 - /** * @brief Set the partial list of services to advertise. * @param [in] v_uuid A vector of 32 bit UUID's to advertise. + * @return True if successful. */ -void NimBLEExtAdvertisement::setPartialServices32(const std::vector& v_uuid) { - setServices(false, 32, v_uuid); +bool NimBLEExtAdvertisement::setPartialServices32(const std::vector& v_uuid) { + return setServices(false, 32, v_uuid); } // setPartialServices32 - /** * @brief Utility function to create the list of service UUID's from a vector. * @param [in] complete If true the vector is the complete set of services. * @param [in] size The bit size of the UUID's in the vector. (16, 32, or 128). - * @param [in] v_uuid The vector of service UUID's to advertise. + * @param [in] uuids The vector of service UUID's to advertise. + * @return True if successful. */ -void NimBLEExtAdvertisement::setServices(const bool complete, const uint8_t size, - const std::vector &v_uuid) -{ - char cdata[2]; - cdata[0] = (size / 8) * v_uuid.size() + 1; - switch(size) { +bool NimBLEExtAdvertisement::setServices(bool complete, uint8_t size, const std::vector& uuids) { + uint8_t header[2]; + uint8_t uuidBytes = size / 8; + header[0] = uuidBytes * uuids.size() + 1; + + switch (size) { case 16: - cdata[1] = complete ? BLE_HS_ADV_TYPE_COMP_UUIDS16 : BLE_HS_ADV_TYPE_INCOMP_UUIDS16; + header[1] = complete ? BLE_HS_ADV_TYPE_COMP_UUIDS16 : BLE_HS_ADV_TYPE_INCOMP_UUIDS16; break; case 32: - cdata[1] = complete ? BLE_HS_ADV_TYPE_COMP_UUIDS32 : BLE_HS_ADV_TYPE_INCOMP_UUIDS32; + header[1] = complete ? BLE_HS_ADV_TYPE_COMP_UUIDS32 : BLE_HS_ADV_TYPE_INCOMP_UUIDS32; break; case 128: - cdata[1] = complete ? BLE_HS_ADV_TYPE_COMP_UUIDS128 : BLE_HS_ADV_TYPE_INCOMP_UUIDS128; + header[1] = complete ? BLE_HS_ADV_TYPE_COMP_UUIDS128 : BLE_HS_ADV_TYPE_INCOMP_UUIDS128; break; default: - return; + NIMBLE_LOGE(LOG_TAG, "Cannot set services, invalid size!"); + return false; } - std::string uuids; - - for(auto &it : v_uuid){ - if(it.bitSize() != size) { - NIMBLE_LOGE(LOG_TAG, "Service UUID(%d) invalid", size); - return; - } else { - switch(size) { - case 16: - uuids += std::string((char*)&it.getNative()->u16.value, 2); - break; - case 32: - uuids += std::string((char*)&it.getNative()->u32.value, 4); - break; - case 128: - uuids += std::string((char*)&it.getNative()->u128.value, 16); - break; - default: - return; + if (addData(header, 2)) { + int count = 0; + for (const auto& uuid : uuids) { + if (uuid.bitSize() != size) { + NIMBLE_LOGE(LOG_TAG, "Service UUID(%d) invalid", size); + continue; + } else { + if (addData(uuid.getValue(), uuidBytes)) { + count++; + } else { + NIMBLE_LOGE(LOG_TAG, "Error setting service UUIDs"); + m_payload.erase(m_payload.end() - 2 - (count * uuidBytes), m_payload.end()); + return false; + } } } + + return true; } - addData(std::string(cdata, 2) + uuids); + return false; } // 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 NimBLEExtAdvertisement::setServiceData(const NimBLEUUID& uuid, const uint8_t* data, size_t length) { + uint8_t uuidBytes = uuid.bitSize() / 8; + uint8_t sDataLen = 2 + uuidBytes + length; + + if (m_payload.size() + sDataLen > CONFIG_BT_NIMBLE_MAX_EXT_ADV_DATA_LEN) { + 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; // removed or not found is still successful + } + + uint8_t header[2]; + header[0] = uuidBytes + length + 1; + header[1] = type; + + // already checked the length above, no need to check here + addData(header, 2); + addData(uuid.getValue(), uuidBytes); + addData(data, length); + return true; +} // 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. */ -void NimBLEExtAdvertisement::setServiceData(const NimBLEUUID &uuid, const std::string &data) { - char cdata[2]; - switch (uuid.bitSize()) { - case 16: { - // [Len] [0x16] [UUID16] data - cdata[0] = data.length() + 3; - cdata[1] = BLE_HS_ADV_TYPE_SVC_DATA_UUID16; // 0x16 - addData(std::string(cdata, 2) + std::string((char*) &uuid.getNative()->u16.value, 2) + data); - break; - } - - case 32: { - // [Len] [0x20] [UUID32] data - cdata[0] = data.length() + 5; - cdata[1] = BLE_HS_ADV_TYPE_SVC_DATA_UUID32; // 0x20 - addData(std::string(cdata, 2) + std::string((char*) &uuid.getNative()->u32.value, 4) + data); - break; - } - - case 128: { - // [Len] [0x21] [UUID128] data - cdata[0] = data.length() + 17; - cdata[1] = BLE_HS_ADV_TYPE_SVC_DATA_UUID128; // 0x21 - addData(std::string(cdata, 2) + std::string((char*) &uuid.getNative()->u128.value, 16) + data); - break; - } - - default: - return; - } +bool NimBLEExtAdvertisement::setServiceData(const NimBLEUUID& uuid, const std::string& data) { + return setServiceData(uuid, reinterpret_cast(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 NimBLEExtAdvertisement::setServiceData(const NimBLEUUID& uuid, const std::vector& data) { + return setServiceData(uuid, &data[0], data.size()); +} // setServiceData /** * @brief Set the short name. * @param [in] name The short name of the device. + * @return True if successful. */ -void NimBLEExtAdvertisement::setShortName(const std::string &name) { - char cdata[2]; - cdata[0] = name.length() + 1; - cdata[1] = BLE_HS_ADV_TYPE_INCOMP_NAME; // 0x08 - addData(std::string(cdata, 2) + name); +bool NimBLEExtAdvertisement::setShortName(const std::string& name) { + return setName(name, false); } // setShortName +/** + * @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 NimBLEExtAdvertisement::setPreferredParams(uint16_t minInterval, uint16_t maxInterval) { + minInterval = std::max(minInterval, 0x6); + minInterval = std::min(minInterval, 0xC80); + maxInterval = std::max(maxInterval, 0x6); + maxInterval = std::min(maxInterval, 0xC80); + maxInterval = std::max(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 Adds Tx power level to the advertisement data. */ -void NimBLEExtAdvertisement::addTxPower() { +bool NimBLEExtAdvertisement::addTxPower() { + if (m_params.legacy_pdu) { + m_params.include_tx_power = 0; + 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); + } + m_params.include_tx_power = 1; + return true; } // addTxPower +/** + * @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 NimBLEExtAdvertisement::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 Set the preferred connection interval parameters. - * @param [in] min The minimum interval desired. - * @param [in] max The maximum interval desired. + * @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. */ -void NimBLEExtAdvertisement::setPreferredParams(uint16_t min, uint16_t max) { - 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] = min; - data[3] = min >> 8; - data[4] = max; - data[5] = max >> 8; - addData(data, 6); -} // setPreferredParams +bool NimBLEExtAdvertisement::removeData(uint8_t type) { + int dataLoc = getDataLocation(type); + if (dataLoc != -1) { + std::vector 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; + } -#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_BROADCASTER && CONFIG_BT_NIMBLE_EXT_ADV */ + return false; +} // removeData + +/** + * @brief Get the size of the current data. + */ +size_t NimBLEExtAdvertisement::getDataSize() const { + return m_payload.size(); +} // getDataSize + +/** + * @brief Get the string representation of the advertisement data. + * @return The string representation of the advertisement data. + */ +std::string NimBLEExtAdvertisement::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 diff --git a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEExtAdvertising.h b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEExtAdvertising.h index b1f21fc78..9824ad199 100644 --- a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEExtAdvertising.h +++ b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEExtAdvertising.h @@ -1,152 +1,163 @@ /* - * NimBLEExtAdvertising.h + * Copyright 2020-2025 Ryan Powell and + * esp-nimble-cpp, NimBLE-Arduino contributors. * - * Created: on February 6, 2022 - * Author H2zero + * 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 MAIN_BLEEXTADVERTISING_H_ -#define MAIN_BLEEXTADVERTISING_H_ -#include "nimconfig.h" -#if defined(CONFIG_BT_ENABLED) && \ - defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER) && \ - CONFIG_BT_NIMBLE_EXT_ADV +#ifndef NIMBLE_CPP_EXTADVERTISING_H_ +#define NIMBLE_CPP_EXTADVERTISING_H_ -# if defined(CONFIG_NIMBLE_CPP_IDF) -# include "host/ble_gap.h" -# else -# include "nimble/nimble/host/include/host/ble_gap.h" -# endif +#include "nimconfig.h" +#if CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_BROADCASTER && CONFIG_BT_NIMBLE_EXT_ADV + +# if defined(CONFIG_NIMBLE_CPP_IDF) +# include "host/ble_gap.h" +# else +# include "nimble/nimble/host/include/host/ble_gap.h" +# endif /**** FIX COMPILATION ****/ -#undef min -#undef max +# undef min +# undef max /**************************/ -#include "NimBLEAddress.h" -#include "NimBLEUUID.h" +# include "NimBLEAddress.h" -#include +# include +# include class NimBLEExtAdvertisingCallbacks; - +class NimBLEUUID; /** * @brief Extended advertisement data */ class NimBLEExtAdvertisement { -public: - NimBLEExtAdvertisement(uint8_t priPhy = BLE_HCI_LE_PHY_1M, - uint8_t secPhy = BLE_HCI_LE_PHY_1M); - void setAppearance(uint16_t appearance); - void setCompleteServices(const NimBLEUUID &uuid); - void setCompleteServices16(const std::vector &v_uuid); - void setCompleteServices32(const std::vector &v_uuid); - void setFlags(uint8_t flag); - void setManufacturerData(const std::string &data); - void setURI(const std::string &uri); - void setName(const std::string &name); - void setPartialServices(const NimBLEUUID &uuid); - void setPartialServices16(const std::vector &v_uuid); - void setPartialServices32(const std::vector &v_uuid); - void setServiceData(const NimBLEUUID &uuid, const std::string &data); - void setShortName(const std::string &name); - void setData(const uint8_t * data, size_t length); - void addData(const std::string &data); - void addData(const uint8_t * data, size_t length); - void addTxPower(); - void setPreferredParams(uint16_t min, uint16_t max); - void setLegacyAdvertising(bool val); - void setConnectable(bool val); - void setScannable(bool val); - void setMinInterval(uint32_t mininterval); - void setMaxInterval(uint32_t maxinterval); - void setPrimaryPhy(uint8_t phy); - void setSecondaryPhy(uint8_t phy); - void setScanFilter(bool scanRequestWhitelistOnly, bool connectWhitelistOnly); - void setDirectedPeer(const NimBLEAddress & addr); - void setDirected(bool val, bool high_duty = true); - void setAnonymous(bool val); - void setPrimaryChannels(bool ch37, bool ch38, bool ch39); - void setTxPower(int8_t dbm); - void setAddress(const NimBLEAddress & addr); - void enableScanRequestCallback(bool enable); - void clearData(); - size_t getDataSize(); + public: + NimBLEExtAdvertisement(uint8_t priPhy = BLE_HCI_LE_PHY_1M, uint8_t secPhy = BLE_HCI_LE_PHY_1M); + bool setAppearance(uint16_t appearance); + bool addServiceUUID(const NimBLEUUID& serviceUUID); + bool addServiceUUID(const char* serviceUUID); + bool removeServiceUUID(const NimBLEUUID& serviceUUID); + bool removeServiceUUID(const char* serviceUUID); + bool removeServices(); + bool setCompleteServices(const NimBLEUUID& uuid); + bool setCompleteServices16(const std::vector& uuids); + bool setCompleteServices32(const std::vector& uuids); + bool setFlags(uint8_t flag); + bool setManufacturerData(const uint8_t* data, size_t length); + bool setManufacturerData(const std::string& data); + bool setManufacturerData(const std::vector& data); + bool setURI(const std::string& uri); + bool setName(const std::string& name, bool isComplete = true); + bool setPartialServices(const NimBLEUUID& uuid); + bool setPartialServices16(const std::vector& uuids); + bool setPartialServices32(const std::vector& 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& data); + bool setShortName(const std::string& name); + bool setData(const uint8_t* data, size_t length); + bool addData(const uint8_t* data, size_t length); + bool addData(const std::string& data); + bool setPreferredParams(uint16_t min, uint16_t max); + bool addTxPower(); + void setLegacyAdvertising(bool enable); + void setConnectable(bool enable); + void setScannable(bool enable); + void setMinInterval(uint32_t mininterval); + void setMaxInterval(uint32_t maxinterval); + void setPrimaryPhy(uint8_t phy); + void setSecondaryPhy(uint8_t phy); + void setScanFilter(bool scanRequestWhitelistOnly, bool connectWhitelistOnly); + void setDirectedPeer(const NimBLEAddress& addr); + void setDirected(bool enable, bool high_duty = true); + void setAnonymous(bool enable); + void setPrimaryChannels(bool ch37, bool ch38, bool ch39); + void setTxPower(int8_t dbm); + void setAddress(const NimBLEAddress& addr); + void enableScanRequestCallback(bool enable); + void clearData(); + int getDataLocation(uint8_t type) const; + bool removeData(uint8_t type); + size_t getDataSize() const; + std::string toString() const; -private: + private: friend class NimBLEExtAdvertising; - void setServices(const bool complete, const uint8_t size, - const std::vector &v_uuid); - - std::vector m_payload; - ble_gap_ext_adv_params m_params; - NimBLEAddress m_advAddress; -}; // NimBLEExtAdvertisement + bool setServices(bool complete, uint8_t size, const std::vector& uuids); + std::vector m_payload{}; + ble_gap_ext_adv_params m_params{}; + NimBLEAddress m_advAddress{}; +}; // NimBLEExtAdvertisement /** * @brief Extended advertising class. */ class NimBLEExtAdvertising { -public: - /** - * @brief Construct an extended advertising object. - */ - NimBLEExtAdvertising() :m_advStatus(CONFIG_BT_NIMBLE_MAX_EXT_ADV_INSTANCES + 1, false) {} + public: + NimBLEExtAdvertising(); ~NimBLEExtAdvertising(); - bool start(uint8_t inst_id, int duration = 0, int max_events = 0); - bool setInstanceData(uint8_t inst_id, NimBLEExtAdvertisement& adv); - bool setScanResponseData(uint8_t inst_id, NimBLEExtAdvertisement & data); - bool removeInstance(uint8_t inst_id); + bool start(uint8_t instId, int duration = 0, int maxEvents = 0); + bool setInstanceData(uint8_t instId, NimBLEExtAdvertisement& adv); + bool setScanResponseData(uint8_t instId, NimBLEExtAdvertisement& data); + bool removeInstance(uint8_t instId); bool removeAll(); - bool stop(uint8_t inst_id); + bool stop(uint8_t instId); bool stop(); - bool isActive(uint8_t inst_id); + bool isActive(uint8_t instId); bool isAdvertising(); - void setCallbacks(NimBLEExtAdvertisingCallbacks* callbacks, - bool deleteCallbacks = true); + void setCallbacks(NimBLEExtAdvertisingCallbacks* callbacks, bool deleteCallbacks = true); -private: + private: friend class NimBLEDevice; friend class NimBLEServer; - void onHostSync(); - static int handleGapEvent(struct ble_gap_event *event, void *arg); + void onHostSync(); + static int handleGapEvent(struct ble_gap_event* event, void* arg); - bool m_scanResp; - bool m_deleteCallbacks; - NimBLEExtAdvertisingCallbacks* m_pCallbacks; - ble_gap_ext_adv_params m_advParams; - std::vector m_advStatus; + bool m_deleteCallbacks; + NimBLEExtAdvertisingCallbacks* m_pCallbacks; + std::vector m_advStatus; }; - /** * @brief Callbacks associated with NimBLEExtAdvertising class. */ class NimBLEExtAdvertisingCallbacks { -public: + public: virtual ~NimBLEExtAdvertisingCallbacks() {}; /** * @brief Handle an advertising stop event. * @param [in] pAdv A convenience pointer to the extended advertising interface. * @param [in] reason The reason code for stopping the advertising. - * @param [in] inst_id The instance ID of the advertisement that was stopped. + * @param [in] instId The instance ID of the advertisement that was stopped. */ - virtual void onStopped(NimBLEExtAdvertising *pAdv, int reason, uint8_t inst_id); + virtual void onStopped(NimBLEExtAdvertising* pAdv, int reason, uint8_t instId); /** * @brief Handle a scan response request. * This is called when a scanning device requests a scan response. * @param [in] pAdv A convenience pointer to the extended advertising interface. - * @param [in] inst_id The instance ID of the advertisement that the scan response request was made. + * @param [in] instId The instance ID of the advertisement that the scan response request was made. * @param [in] addr The address of the device making the request. */ - virtual void onScanRequest(NimBLEExtAdvertising *pAdv, uint8_t inst_id, NimBLEAddress addr); + virtual void onScanRequest(NimBLEExtAdvertising* pAdv, uint8_t instId, NimBLEAddress addr); }; // NimBLEExtAdvertisingCallbacks -#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_BROADCASTER && CONFIG_BT_NIMBLE_EXT_ADV */ -#endif /* MAIN_BLEADVERTISING_H_ */ +#endif // CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_BROADCASTER && CONFIG_BT_NIMBLE_EXT_ADV +#endif // NIMBLE_CPP_EXTADVERTISING_H_ diff --git a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEHIDDevice.cpp b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEHIDDevice.cpp index 12c14def1..73393930d 100644 --- a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEHIDDevice.cpp +++ b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEHIDDevice.cpp @@ -1,111 +1,111 @@ /* - * NimBLEHIDDevice.cpp + * Copyright 2020-2025 Ryan Powell and + * esp-nimble-cpp, NimBLE-Arduino contributors. * - * Created: on Oct 06 2020 - * Author wakwak-koba + * 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 * - * Originally: + * http://www.apache.org/licenses/LICENSE-2.0 * - * BLEHIDDevice.cpp - * - * Created on: Jan 03, 2018 - * Author: chegewara + * 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 "nimconfig.h" -#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) - #include "NimBLEHIDDevice.h" -#include "NimBLE2904.h" +#if CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_BROADCASTER && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL + +# include "NimBLEServer.h" +# include "NimBLEService.h" +# include "NimBLE2904.h" + +static constexpr uint16_t deviceInfoSvcUuid = 0x180a; +static constexpr uint16_t hidSvcUuid = 0x1812; +static constexpr uint16_t batterySvcUuid = 0x180f; + +static constexpr uint16_t pnpCharUuid = 0x2a50; +static constexpr uint16_t hidInfoCharUuid = 0x2a4a; +static constexpr uint16_t reportMapCharUuid = 0x2a4b; +static constexpr uint16_t hidControlCharUuid = 0x2a4c; +static constexpr uint16_t inputReportChrUuid = 0x2a4d; +static constexpr uint16_t protocolModeCharUuid = 0x2a4e; +static constexpr uint16_t batteryLevelCharUuid = 0x2a19; +static constexpr uint16_t batteryLevelDscUuid = 0x2904; +static constexpr uint16_t featureReportDscUuid = 0x2908; +static constexpr uint16_t m_manufacturerChrUuid = 0x2a29; +static constexpr uint16_t bootInputChrUuid = 0x2a22; +static constexpr uint16_t bootOutputChrUuid = 0x2a32; /** * @brief Construct a default NimBLEHIDDevice object. * @param [in] server A pointer to the server instance this HID Device will use. */ NimBLEHIDDevice::NimBLEHIDDevice(NimBLEServer* server) { - /* - * Here we create mandatory services described in bluetooth specification - */ - m_deviceInfoService = server->createService(NimBLEUUID((uint16_t)0x180a)); - m_hidService = server->createService(NimBLEUUID((uint16_t)0x1812)); - m_batteryService = server->createService(NimBLEUUID((uint16_t)0x180f)); + // Here we create mandatory services described in bluetooth specification + m_deviceInfoSvc = server->createService(deviceInfoSvcUuid); + m_hidSvc = server->createService(hidSvcUuid); + m_batterySvc = server->createService(batterySvcUuid); - /* - * Mandatory characteristic for device info service - */ - m_pnpCharacteristic = m_deviceInfoService->createCharacteristic((uint16_t)0x2a50, NIMBLE_PROPERTY::READ); + // Mandatory characteristic for device info service + m_pnpChr = m_deviceInfoSvc->createCharacteristic(pnpCharUuid, NIMBLE_PROPERTY::READ); - /* - * Non-mandatory characteristics for device info service - * Will be created on demand - */ - m_manufacturerCharacteristic = nullptr; + // Mandatory characteristics for HID service + m_hidInfoChr = m_hidSvc->createCharacteristic(hidInfoCharUuid, NIMBLE_PROPERTY::READ); + m_reportMapChr = m_hidSvc->createCharacteristic(reportMapCharUuid, NIMBLE_PROPERTY::READ); + m_hidControlChr = m_hidSvc->createCharacteristic(hidControlCharUuid, NIMBLE_PROPERTY::WRITE_NR); + m_protocolModeChr = + m_hidSvc->createCharacteristic(protocolModeCharUuid, NIMBLE_PROPERTY::WRITE_NR | NIMBLE_PROPERTY::READ); - /* - * Mandatory characteristics for HID service - */ - m_hidInfoCharacteristic = m_hidService->createCharacteristic((uint16_t)0x2a4a, NIMBLE_PROPERTY::READ); - m_reportMapCharacteristic = m_hidService->createCharacteristic((uint16_t)0x2a4b, NIMBLE_PROPERTY::READ); - m_hidControlCharacteristic = m_hidService->createCharacteristic((uint16_t)0x2a4c, NIMBLE_PROPERTY::WRITE_NR); - m_protocolModeCharacteristic = m_hidService->createCharacteristic((uint16_t)0x2a4e, NIMBLE_PROPERTY::WRITE_NR | NIMBLE_PROPERTY::READ); + // Mandatory battery level characteristic with notification and presence descriptor + m_batteryLevelChr = + m_batterySvc->createCharacteristic(batteryLevelCharUuid, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::NOTIFY); + NimBLE2904* batteryLevelDescriptor = m_batteryLevelChr->create2904(); + batteryLevelDescriptor->setFormat(NimBLE2904::FORMAT_UINT8); + batteryLevelDescriptor->setUnit(0x27ad); // percentage - /* - * Mandatory battery level characteristic with notification and presence descriptor - */ - m_batteryLevelCharacteristic = m_batteryService->createCharacteristic((uint16_t)0x2a19, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::NOTIFY); - NimBLE2904 *batteryLevelDescriptor = (NimBLE2904*)m_batteryLevelCharacteristic->createDescriptor((uint16_t)0x2904); - batteryLevelDescriptor->setFormat(NimBLE2904::FORMAT_UINT8); - batteryLevelDescriptor->setNamespace(1); - batteryLevelDescriptor->setUnit(0x27ad); - - /* - * This value is setup here because its default value in most usage cases, its very rare to use boot mode - * and we want to simplify library using as much as possible - */ - const uint8_t pMode[] = {0x01}; - protocolMode()->setValue((uint8_t*)pMode, 1); -} - -NimBLEHIDDevice::~NimBLEHIDDevice() { -} + // This value is setup here because its default value in most usage cases, it's very rare to use boot mode + m_protocolModeChr->setValue(static_cast(0x01)); +} // NimBLEHIDDevice /** * @brief Set the report map data formatting information. * @param [in] map A pointer to an array with the values to set. * @param [in] size The number of values in the array. */ -void NimBLEHIDDevice::reportMap(uint8_t* map, uint16_t size) { - m_reportMapCharacteristic->setValue(map, size); -} +void NimBLEHIDDevice::setReportMap(uint8_t* map, uint16_t size) { + m_reportMapChr->setValue(map, size); +} // setReportMap /** - * @brief Start the HID device services.\n + * @brief Start the HID device services. * This function called when all the services have been created. */ void NimBLEHIDDevice::startServices() { - m_deviceInfoService->start(); - m_hidService->start(); - m_batteryService->start(); -} + m_deviceInfoSvc->start(); + m_hidSvc->start(); + m_batterySvc->start(); +} // startServices /** - * @brief Create a manufacturer characteristic (this characteristic is optional). + * @brief Get the manufacturer characteristic (this characteristic is optional). + * @details The characteristic will be created if not already existing. + * @returns True if the name was set and/or the characteristic created. */ -NimBLECharacteristic* NimBLEHIDDevice::manufacturer() { - if (m_manufacturerCharacteristic == nullptr) { - m_manufacturerCharacteristic = m_deviceInfoService->createCharacteristic((uint16_t)0x2a29, NIMBLE_PROPERTY::READ); - } +bool NimBLEHIDDevice::setManufacturer(const std::string& name) { + if (m_manufacturerChr == nullptr) { + m_manufacturerChr = m_deviceInfoSvc->createCharacteristic(m_manufacturerChrUuid, NIMBLE_PROPERTY::READ); + } - return m_manufacturerCharacteristic; -} + if (m_manufacturerChr) { + m_manufacturerChr->setValue(name); + return true; + } -/** - * @brief Set manufacturer name - * @param [in] name The manufacturer name of this HID device. - */ -void NimBLEHIDDevice::manufacturer(std::string name) { - manufacturer()->setValue(name); -} + return false; +} // setManufacturer /** * @brief Sets the Plug n Play characteristic value. @@ -114,152 +114,230 @@ void NimBLEHIDDevice::manufacturer(std::string name) { * @param [in] pid The product ID number. * @param [in] version The produce version number. */ -void NimBLEHIDDevice::pnp(uint8_t sig, uint16_t vid, uint16_t pid, uint16_t version) { - uint8_t pnp[] = { - sig, - ((uint8_t*)&vid)[0], - ((uint8_t*)&vid)[1], - ((uint8_t*)&pid)[0], - ((uint8_t*)&pid)[1], - ((uint8_t*)&version)[0], - ((uint8_t*)&version)[1] - }; - m_pnpCharacteristic->setValue(pnp, sizeof(pnp)); -} +void NimBLEHIDDevice::setPnp(uint8_t sig, uint16_t vid, uint16_t pid, uint16_t version) { + uint8_t pnp[] = {sig, + static_cast(vid & 0xFF), + static_cast((vid >> 8) & 0xFF), + static_cast(pid & 0xFF), + static_cast((pid >> 8) & 0xFF), + static_cast(version & 0xFF), + static_cast((version >> 8) & 0xFF)}; + + m_pnpChr->setValue(pnp, sizeof(pnp)); +} // setPnp /** * @brief Sets the HID Information characteristic value. * @param [in] country The country code for the device. * @param [in] flags The HID Class Specification release number to use. */ -void NimBLEHIDDevice::hidInfo(uint8_t country, uint8_t flags) { - uint8_t info[] = {0x11, 0x1, country, flags}; - m_hidInfoCharacteristic->setValue(info, sizeof(info)); -} - -/** - * @brief Create input report characteristic - * @param [in] reportID input report ID, the same as in report map for input object related to the characteristic - * @return pointer to new input report characteristic - */ -NimBLECharacteristic* NimBLEHIDDevice::inputReport(uint8_t reportID) { - NimBLECharacteristic *inputReportCharacteristic = m_hidService->createCharacteristic((uint16_t)0x2a4d, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::NOTIFY | NIMBLE_PROPERTY::READ_ENC); - NimBLEDescriptor *inputReportDescriptor = inputReportCharacteristic->createDescriptor((uint16_t)0x2908, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::READ_ENC); - - uint8_t desc1_val[] = {reportID, 0x01}; - inputReportDescriptor->setValue((uint8_t*)desc1_val, 2); - - return inputReportCharacteristic; -} - -/** - * @brief Create output report characteristic - * @param [in] reportID Output report ID, the same as in report map for output object related to the characteristic - * @return Pointer to new output report characteristic - */ -NimBLECharacteristic* NimBLEHIDDevice::outputReport(uint8_t reportID) { - NimBLECharacteristic *outputReportCharacteristic = m_hidService->createCharacteristic((uint16_t)0x2a4d, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::WRITE_NR | NIMBLE_PROPERTY::READ_ENC | NIMBLE_PROPERTY::WRITE_ENC); - NimBLEDescriptor *outputReportDescriptor = outputReportCharacteristic->createDescriptor((uint16_t)0x2908, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::READ_ENC | NIMBLE_PROPERTY::WRITE_ENC); - - uint8_t desc1_val[] = {reportID, 0x02}; - outputReportDescriptor->setValue((uint8_t*)desc1_val, 2); - - return outputReportCharacteristic; -} - -/** - * @brief Create feature report characteristic. - * @param [in] reportID Feature report ID, the same as in report map for feature object related to the characteristic - * @return Pointer to new feature report characteristic - */ -NimBLECharacteristic* NimBLEHIDDevice::featureReport(uint8_t reportID) { - NimBLECharacteristic *featureReportCharacteristic = m_hidService->createCharacteristic((uint16_t)0x2a4d, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::READ_ENC | NIMBLE_PROPERTY::WRITE_ENC); - NimBLEDescriptor *featureReportDescriptor = featureReportCharacteristic->createDescriptor((uint16_t)0x2908, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::READ_ENC | NIMBLE_PROPERTY::WRITE_ENC); - - uint8_t desc1_val[] = {reportID, 0x03}; - featureReportDescriptor->setValue((uint8_t*)desc1_val, 2); - - return featureReportCharacteristic; -} - -/** - * @brief Creates a keyboard boot input report characteristic - */ -NimBLECharacteristic* NimBLEHIDDevice::bootInput() { - return m_hidService->createCharacteristic((uint16_t)0x2a22, NIMBLE_PROPERTY::NOTIFY); -} - -/** - * @brief Create a keyboard boot output report characteristic - */ -NimBLECharacteristic* NimBLEHIDDevice::bootOutput() { - return m_hidService->createCharacteristic((uint16_t)0x2a32, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::WRITE_NR); -} - -/** - * @brief Returns a pointer to the HID control point characteristic. - */ -NimBLECharacteristic* NimBLEHIDDevice::hidControl() { - return m_hidControlCharacteristic; -} - -/** - * @brief Returns a pointer to the protocol mode characteristic. - */ -NimBLECharacteristic* NimBLEHIDDevice::protocolMode() { - return m_protocolModeCharacteristic; -} +void NimBLEHIDDevice::setHidInfo(uint8_t country, uint8_t flags) { + uint8_t info[] = {0x11, 0x1, country, flags}; + m_hidInfoChr->setValue(info, sizeof(info)); +} // setHidInfo /** * @brief Set the battery level characteristic value. * @param [in] level The battery level value. + * @param [in] notify If true sends a notification to the peer device, otherwise not. default = false */ -void NimBLEHIDDevice::setBatteryLevel(uint8_t level) { - m_batteryLevelCharacteristic->setValue(&level, 1); -} -/* - * @brief Returns battery level characteristic - * @ return battery level characteristic - */ -NimBLECharacteristic* NimBLEHIDDevice::batteryLevel() { - return m_batteryLevelCharacteristic; -} - -/* - -BLECharacteristic* BLEHIDDevice::reportMap() { - return m_reportMapCharacteristic; -} - -BLECharacteristic* BLEHIDDevice::pnp() { - return m_pnpCharacteristic; -} - - -BLECharacteristic* BLEHIDDevice::hidInfo() { - return m_hidInfoCharacteristic; -} -*/ +void NimBLEHIDDevice::setBatteryLevel(uint8_t level, bool notify) { + m_batteryLevelChr->setValue(&level, 1); + if (notify) { + m_batteryLevelChr->notify(); + } +} // setBatteryLevel /** - * @brief Returns a pointer to the device information service. + * @brief Locate the characteristic for a report ID and a report type. + * + * @param [in] reportId Report identifier to locate. + * @param [in] reportType Type of report (input/output/feature). + * @return NimBLECharacteristic* The characteristic. + * @return nullptr If the characteristic does not exist. */ -NimBLEService* NimBLEHIDDevice::deviceInfo() { - return m_deviceInfoService; +NimBLECharacteristic* NimBLEHIDDevice::locateReportCharacteristicByIdAndType(uint8_t reportId, uint8_t reportType) { + NimBLECharacteristic* candidate = m_hidSvc->getCharacteristic(inputReportChrUuid, 0); + for (uint16_t i = 1; (candidate != nullptr) && (i != 0); i++) { + NimBLEDescriptor* dsc = candidate->getDescriptorByUUID(featureReportDscUuid); + NimBLEAttValue desc1_val_att = dsc->getValue(); + const uint8_t* desc1_val = desc1_val_att.data(); + if ((desc1_val[0] == reportId) && (desc1_val[1] == reportType)) return candidate; + candidate = m_hidSvc->getCharacteristic(inputReportChrUuid, i); + } + return nullptr; } /** - * @brief Returns a pointer to the HID service. + * @brief Get the input report characteristic. + * @param [in] reportId Input report ID, the same as in report map for input object related to the characteristic. + * @return NimBLECharacteristic* A pointer to the input report characteristic. + * Store this value to avoid computational overhead. + * @details This will create the characteristic if not already created. */ -NimBLEService* NimBLEHIDDevice::hidService() { - return m_hidService; -} +NimBLECharacteristic* NimBLEHIDDevice::getInputReport(uint8_t reportId) { + NimBLECharacteristic* inputReportChr = locateReportCharacteristicByIdAndType(reportId, 0x01); + if (inputReportChr == nullptr) { + inputReportChr = + m_hidSvc->createCharacteristic(inputReportChrUuid, + NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::NOTIFY | NIMBLE_PROPERTY::READ_ENC); + NimBLEDescriptor* inputReportDsc = + inputReportChr->createDescriptor(featureReportDscUuid, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::READ_ENC); + + uint8_t desc1_val[] = {reportId, 0x01}; + inputReportDsc->setValue(desc1_val, 2); + } + + return inputReportChr; +} // getInputReport /** - * @brief @brief Returns a pointer to the battery service. + * @brief Get the output report characteristic. + * @param [in] reportId Output report ID, the same as in report map for output object related to the characteristic. + * @return NimBLECharacteristic* A pointer to the output report characteristic. + * Store this value to avoid computational overhead. + * @details This will create the characteristic if not already created. */ -NimBLEService* NimBLEHIDDevice::batteryService() { - return m_batteryService; -} +NimBLECharacteristic* NimBLEHIDDevice::getOutputReport(uint8_t reportId) { + NimBLECharacteristic* outputReportChr = locateReportCharacteristicByIdAndType(reportId, 0x02); + if (outputReportChr == nullptr) { + outputReportChr = + m_hidSvc->createCharacteristic(inputReportChrUuid, + NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::WRITE_NR | + NIMBLE_PROPERTY::READ_ENC | NIMBLE_PROPERTY::WRITE_ENC); + NimBLEDescriptor* outputReportDsc = outputReportChr->createDescriptor( + featureReportDscUuid, + NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::READ_ENC | NIMBLE_PROPERTY::WRITE_ENC); + uint8_t desc1_val[] = {reportId, 0x02}; + outputReportDsc->setValue(desc1_val, 2); + } -#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL */ + return outputReportChr; +} // getOutputReport + +/** + * @brief Get the feature report characteristic. + * @param [in] reportId Feature report ID, the same as in report map for feature object related to the characteristic. + * @return NimBLECharacteristic* A pointer to feature report characteristic. + * Store this value to avoid computational overhead. + * @details This will create the characteristic if not already created. + */ +NimBLECharacteristic* NimBLEHIDDevice::getFeatureReport(uint8_t reportId) { + NimBLECharacteristic* featureReportChr = locateReportCharacteristicByIdAndType(reportId, 0x03); + if (featureReportChr == nullptr) { + featureReportChr = m_hidSvc->createCharacteristic( + inputReportChrUuid, + NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::READ_ENC | NIMBLE_PROPERTY::WRITE_ENC); + NimBLEDescriptor* featureReportDsc = featureReportChr->createDescriptor( + featureReportDscUuid, + NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::READ_ENC | NIMBLE_PROPERTY::WRITE_ENC); + + uint8_t desc1_val[] = {reportId, 0x03}; + featureReportDsc->setValue(desc1_val, 2); + } + + return featureReportChr; +} // getFeatureReport + +/** + * @brief Get a keyboard boot input report characteristic. + * @returns A pointer to the boot input report characteristic, or nullptr on error. + * @details This will create the characteristic if not already created. + */ +NimBLECharacteristic* NimBLEHIDDevice::getBootInput() { + NimBLECharacteristic* bootInputChr = m_hidSvc->getCharacteristic(bootInputChrUuid); + if (bootInputChr) { + return bootInputChr; + } + + return m_hidSvc->createCharacteristic(bootInputChrUuid, NIMBLE_PROPERTY::NOTIFY); +} // getBootInput + +/** + * @brief Create a keyboard boot output report characteristic + * @returns A pointer to the boot output report characteristic, or nullptr on error. + * @details This will create the characteristic if not already created. + */ +NimBLECharacteristic* NimBLEHIDDevice::getBootOutput() { + NimBLECharacteristic* bootOutputChr = m_hidSvc->getCharacteristic(bootOutputChrUuid); + if (bootOutputChr) { + return bootOutputChr; + } + + return m_hidSvc->createCharacteristic(bootOutputChrUuid, + NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::WRITE_NR); +} // getBootOutput + +/** + * @brief Get the HID control point characteristic. + * @returns A pointer to the HID control point characteristic. + */ +NimBLECharacteristic* NimBLEHIDDevice::getHidControl() { + return m_hidControlChr; +} // getHidControl + +/** + * @brief Get the HID protocol mode characteristic. + * @returns a pointer to the protocol mode characteristic. + */ +NimBLECharacteristic* NimBLEHIDDevice::getProtocolMode() { + return m_protocolModeChr; +} // getProtocolMode + +/** + * @brief Get the battery level characteristic + * @returns A pointer to the battery level characteristic + */ +NimBLECharacteristic* NimBLEHIDDevice::getBatteryLevel() { + return m_batteryLevelChr; +} // getBatteryLevel + +/** + * @brief Get the report map characteristic. + * @returns A pointer to the report map characteristic. + */ +NimBLECharacteristic* NimBLEHIDDevice::getReportMap() { + return m_reportMapChr; +} // getReportMap + +/** + * @brief Get the PnP characteristic. + * @returns A pointer to the PnP characteristic. + */ +NimBLECharacteristic* NimBLEHIDDevice::getPnp() { + return m_pnpChr; +} // getPnp + +/** + * @brief Get the HID information characteristic. + * @returns A pointer to the HID information characteristic. + */ +NimBLECharacteristic* NimBLEHIDDevice::getHidInfo() { + return m_hidInfoChr; +} // hidInfo + +/** + * @brief Get the manufacturer characteristic. + * @returns A pointer to the manufacturer characteristic. + */ +NimBLEService* NimBLEHIDDevice::getDeviceInfoService() { + return m_deviceInfoSvc; +} // getDeviceInfoService + +/** + * @brief Get the HID service. + * @returns A pointer to the HID service. + */ +NimBLEService* NimBLEHIDDevice::getHidService() { + return m_hidSvc; +} // getHidService + +/** + * @brief Get the battery service. + * @returns A pointer to the battery service. + */ +NimBLEService* NimBLEHIDDevice::getBatteryService() { + return m_batterySvc; +} // getBatteryService + +#endif // CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL diff --git a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEHIDDevice.h b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEHIDDevice.h index 6461a4f32..cbc9839d6 100644 --- a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEHIDDevice.h +++ b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEHIDDevice.h @@ -1,87 +1,89 @@ /* - * NimBLEHIDDevice.h + * Copyright 2020-2025 Ryan Powell and + * esp-nimble-cpp, NimBLE-Arduino contributors. * - * Created: on Oct 06 2020 - * Author wakwak-koba + * 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 * - * Originally: + * http://www.apache.org/licenses/LICENSE-2.0 * - * BLEHIDDevice.h - * - * Created on: Jan 03, 2018 - * Author: chegewara + * 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 _BLEHIDDEVICE_H_ -#define _BLEHIDDEVICE_H_ +#ifndef NIMBLE_CPP_HIDDEVICE_H_ +#define NIMBLE_CPP_HIDDEVICE_H_ #include "nimconfig.h" -#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER) +#if CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_BROADCASTER && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL -#include "NimBLECharacteristic.h" -#include "NimBLEService.h" -#include "NimBLEDescriptor.h" -#include "HIDTypes.h" +# include +# include -#define GENERIC_HID 0x03C0 -#define HID_KEYBOARD 0x03C1 -#define HID_MOUSE 0x03C2 -#define HID_JOYSTICK 0x03C3 -#define HID_GAMEPAD 0x03C4 -#define HID_TABLET 0x03C5 -#define HID_CARD_READER 0x03C6 -#define HID_DIGITAL_PEN 0x03C7 -#define HID_BARCODE 0x03C8 +# define GENERIC_HID 0x03C0 +# define HID_KEYBOARD 0x03C1 +# define HID_MOUSE 0x03C2 +# define HID_JOYSTICK 0x03C3 +# define HID_GAMEPAD 0x03C4 +# define HID_TABLET 0x03C5 +# define HID_CARD_READER 0x03C6 +# define HID_DIGITAL_PEN 0x03C7 +# define HID_BARCODE 0x03C8 -#define PNPVersionField(MajorVersion, MinorVersion, PatchVersion) ((MajorVersion << 16) & 0xFF00) | ((MinorVersion << 8) & 0x00F0) | (PatchVersion & 0x000F) +# define PNPVersionField(MajorVersion, MinorVersion, PatchVersion) \ + ((MajorVersion << 16) & 0xFF00) | ((MinorVersion << 8) & 0x00F0) | (PatchVersion & 0x000F) + +class NimBLEServer; +class NimBLEService; +class NimBLECharacteristic; /** - * @brief A model of a %BLE Human Interface Device. + * @brief A model of a BLE Human Interface Device. */ class NimBLEHIDDevice { -public: - NimBLEHIDDevice(NimBLEServer*); - virtual ~NimBLEHIDDevice(); + public: + NimBLEHIDDevice(NimBLEServer* server); - void reportMap(uint8_t* map, uint16_t); - void startServices(); + void setReportMap(uint8_t* map, uint16_t); + void startServices(); + bool setManufacturer(const std::string& name); + void setPnp(uint8_t sig, uint16_t vid, uint16_t pid, uint16_t version); + void setHidInfo(uint8_t country, uint8_t flags); + void setBatteryLevel(uint8_t level, bool notify = false); + NimBLECharacteristic* getBatteryLevel(); + NimBLECharacteristic* getReportMap(); + NimBLECharacteristic* getHidControl(); + NimBLECharacteristic* getInputReport(uint8_t reportId); + NimBLECharacteristic* getOutputReport(uint8_t reportId); + NimBLECharacteristic* getFeatureReport(uint8_t reportId); + NimBLECharacteristic* getProtocolMode(); + NimBLECharacteristic* getBootInput(); + NimBLECharacteristic* getBootOutput(); + NimBLECharacteristic* getPnp(); + NimBLECharacteristic* getHidInfo(); + NimBLEService* getDeviceInfoService(); + NimBLEService* getHidService(); + NimBLEService* getBatteryService(); - NimBLEService* deviceInfo(); - NimBLEService* hidService(); - NimBLEService* batteryService(); + private: + NimBLEService* m_deviceInfoSvc{nullptr}; // 0x180a + NimBLEService* m_hidSvc{nullptr}; // 0x1812 + NimBLEService* m_batterySvc{nullptr}; // 0x180f - NimBLECharacteristic* manufacturer(); - void manufacturer(std::string name); - //NimBLECharacteristic* pnp(); - void pnp(uint8_t sig, uint16_t vid, uint16_t pid, uint16_t version); - //NimBLECharacteristic* hidInfo(); - void hidInfo(uint8_t country, uint8_t flags); - NimBLECharacteristic* batteryLevel(); - void setBatteryLevel(uint8_t level); + NimBLECharacteristic* m_manufacturerChr{nullptr}; // 0x2a29 + NimBLECharacteristic* m_pnpChr{nullptr}; // 0x2a50 + NimBLECharacteristic* m_hidInfoChr{nullptr}; // 0x2a4a + NimBLECharacteristic* m_reportMapChr{nullptr}; // 0x2a4b + NimBLECharacteristic* m_hidControlChr{nullptr}; // 0x2a4c + NimBLECharacteristic* m_protocolModeChr{nullptr}; // 0x2a4e + NimBLECharacteristic* m_batteryLevelChr{nullptr}; // 0x2a19 - - //NimBLECharacteristic* reportMap(); - NimBLECharacteristic* hidControl(); - NimBLECharacteristic* inputReport(uint8_t reportID); - NimBLECharacteristic* outputReport(uint8_t reportID); - NimBLECharacteristic* featureReport(uint8_t reportID); - NimBLECharacteristic* protocolMode(); - NimBLECharacteristic* bootInput(); - NimBLECharacteristic* bootOutput(); - -private: - NimBLEService* m_deviceInfoService; //0x180a - NimBLEService* m_hidService; //0x1812 - NimBLEService* m_batteryService = 0; //0x180f - - NimBLECharacteristic* m_manufacturerCharacteristic; //0x2a29 - NimBLECharacteristic* m_pnpCharacteristic; //0x2a50 - NimBLECharacteristic* m_hidInfoCharacteristic; //0x2a4a - NimBLECharacteristic* m_reportMapCharacteristic; //0x2a4b - NimBLECharacteristic* m_hidControlCharacteristic; //0x2a4c - NimBLECharacteristic* m_protocolModeCharacteristic; //0x2a4e - NimBLECharacteristic* m_batteryLevelCharacteristic; //0x2a19 + NimBLECharacteristic* locateReportCharacteristicByIdAndType(uint8_t reportId, uint8_t reportType); }; -#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_BROADCASTER */ -#endif /* _BLEHIDDEVICE_H_ */ +#endif // CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_BROADCASTER && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL +#endif // NIMBLE_CPP_HIDDEVICE_H_ diff --git a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEL2CAPChannel.cpp b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEL2CAPChannel.cpp new file mode 100644 index 000000000..ef290d1a2 --- /dev/null +++ b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEL2CAPChannel.cpp @@ -0,0 +1,314 @@ +// +// (C) Dr. Michael 'Mickey' Lauer +// + +#include "NimBLEL2CAPChannel.h" +#if CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_L2CAP_COC_MAX_NUM + +# include "NimBLEClient.h" +# include "NimBLELog.h" +# include "NimBLEUtils.h" + +# if defined(CONFIG_NIMBLE_CPP_IDF) +# include "host/ble_gap.h" +# else +# include "nimble/nimble/host/include/host/ble_gap.h" +# endif + +// L2CAP buffer block size +# define L2CAP_BUF_BLOCK_SIZE (250) +# define L2CAP_BUF_SIZE_MTUS_PER_CHANNEL (3) +// Round-up integer division +# define CEIL_DIVIDE(a, b) (((a) + (b) - 1) / (b)) +# define ROUND_DIVIDE(a, b) (((a) + (b) / 2) / (b)) +// Retry +constexpr uint32_t RetryTimeout = 50; +constexpr int RetryCounter = 3; + +NimBLEL2CAPChannel::NimBLEL2CAPChannel(uint16_t psm, uint16_t mtu, NimBLEL2CAPChannelCallbacks* callbacks) + : psm(psm), mtu(mtu), callbacks(callbacks) { + assert(mtu); // fail here, if MTU is too little + assert(callbacks); // fail here, if no callbacks are given + assert(setupMemPool()); // fail here, if the memory pool could not be setup + + NIMBLE_LOGI(LOG_TAG, "L2CAP COC 0x%04X initialized w/ L2CAP MTU %i", this->psm, this->mtu); +}; + +NimBLEL2CAPChannel::~NimBLEL2CAPChannel() { + teardownMemPool(); + + NIMBLE_LOGI(LOG_TAG, "L2CAP COC 0x%04X shutdown and freed.", this->psm); +} + +bool NimBLEL2CAPChannel::setupMemPool() { + const size_t buf_blocks = CEIL_DIVIDE(mtu, L2CAP_BUF_BLOCK_SIZE) * L2CAP_BUF_SIZE_MTUS_PER_CHANNEL; + NIMBLE_LOGD(LOG_TAG, "Computed number of buf_blocks = %d", buf_blocks); + + _coc_memory = malloc(OS_MEMPOOL_SIZE(buf_blocks, L2CAP_BUF_BLOCK_SIZE) * sizeof(os_membuf_t)); + if (_coc_memory == 0) { + NIMBLE_LOGE(LOG_TAG, "Can't allocate _coc_memory: %d", errno); + return false; + } + + auto rc = os_mempool_init(&_coc_mempool, buf_blocks, L2CAP_BUF_BLOCK_SIZE, _coc_memory, "appbuf"); + if (rc != 0) { + NIMBLE_LOGE(LOG_TAG, "Can't os_mempool_init: %d", rc); + return false; + } + + auto rc2 = os_mbuf_pool_init(&_coc_mbuf_pool, &_coc_mempool, L2CAP_BUF_BLOCK_SIZE, buf_blocks); + if (rc2 != 0) { + NIMBLE_LOGE(LOG_TAG, "Can't os_mbuf_pool_init: %d", rc); + return false; + } + + this->receiveBuffer = (uint8_t*)malloc(mtu); + if (!this->receiveBuffer) { + NIMBLE_LOGE(LOG_TAG, "Can't malloc receive buffer: %d, %s", errno, strerror(errno)); + return false; + } + + return true; +} + +void NimBLEL2CAPChannel::teardownMemPool() { + if (this->callbacks) { + delete this->callbacks; + } + if (this->receiveBuffer) { + free(this->receiveBuffer); + } + if (_coc_memory) { + free(_coc_memory); + } +} + +int NimBLEL2CAPChannel::writeFragment(std::vector::const_iterator begin, std::vector::const_iterator end) { + auto toSend = end - begin; + + if (stalled) { + NIMBLE_LOGD(LOG_TAG, "L2CAP Channel waiting for unstall..."); + NimBLETaskData taskData; + m_pTaskData = &taskData; + NimBLEUtils::taskWait(taskData, BLE_NPL_TIME_FOREVER); + m_pTaskData = nullptr; + stalled = false; + NIMBLE_LOGD(LOG_TAG, "L2CAP Channel unstalled!"); + } + + struct ble_l2cap_chan_info info; + ble_l2cap_get_chan_info(channel, &info); + // Take the minimum of our and peer MTU + auto mtu = info.peer_coc_mtu < info.our_coc_mtu ? info.peer_coc_mtu : info.our_coc_mtu; + + if (toSend > mtu) { + return -BLE_HS_EBADDATA; + } + + auto retries = RetryCounter; + + while (retries--) { + auto txd = os_mbuf_get_pkthdr(&_coc_mbuf_pool, 0); + if (!txd) { + NIMBLE_LOGE(LOG_TAG, "Can't os_mbuf_get_pkthdr."); + return -BLE_HS_ENOMEM; + } + auto append = os_mbuf_append(txd, &(*begin), toSend); + if (append != 0) { + NIMBLE_LOGE(LOG_TAG, "Can't os_mbuf_append: %d", append); + return append; + } + + auto res = ble_l2cap_send(channel, txd); + switch (res) { + case 0: + NIMBLE_LOGD(LOG_TAG, "L2CAP COC 0x%04X sent %d bytes.", this->psm, toSend); + return 0; + + case BLE_HS_ESTALLED: + stalled = true; + NIMBLE_LOGD(LOG_TAG, "L2CAP COC 0x%04X sent %d bytes.", this->psm, toSend); + NIMBLE_LOGW(LOG_TAG, + "ble_l2cap_send returned BLE_HS_ESTALLED. Next send will wait for unstalled event..."); + return 0; + + case BLE_HS_ENOMEM: + case BLE_HS_EAGAIN: + case BLE_HS_EBUSY: + NIMBLE_LOGD(LOG_TAG, "ble_l2cap_send returned %d. Retrying shortly...", res); + os_mbuf_free_chain(txd); + ble_npl_time_delay(ble_npl_time_ms_to_ticks32(RetryTimeout)); + continue; + + default: + NIMBLE_LOGE(LOG_TAG, "ble_l2cap_send failed: %d", res); + return res; + } + } + NIMBLE_LOGE(LOG_TAG, "Retries exhausted, dropping %d bytes to send.", toSend); + return -BLE_HS_EREJECT; +} + +# if CONFIG_BT_NIMBLE_ROLE_CENTRAL +NimBLEL2CAPChannel* NimBLEL2CAPChannel::connect(NimBLEClient* client, + uint16_t psm, + uint16_t mtu, + NimBLEL2CAPChannelCallbacks* callbacks) { + if (!client->isConnected()) { + NIMBLE_LOGE( + LOG_TAG, + "Client is not connected. Before connecting via L2CAP, a GAP connection must have been established"); + return nullptr; + }; + + auto channel = new NimBLEL2CAPChannel(psm, mtu, callbacks); + + auto sdu_rx = os_mbuf_get_pkthdr(&channel->_coc_mbuf_pool, 0); + if (!sdu_rx) { + NIMBLE_LOGE(LOG_TAG, "Can't allocate SDU buffer: %d, %s", errno, strerror(errno)); + return nullptr; + } + auto rc = ble_l2cap_connect(client->getConnHandle(), psm, mtu, sdu_rx, NimBLEL2CAPChannel::handleL2capEvent, channel); + if (rc != 0) { + NIMBLE_LOGE(LOG_TAG, "ble_l2cap_connect failed: %d", rc); + } + return channel; +} +# endif // CONFIG_BT_NIMBLE_ROLE_CENTRAL + +bool NimBLEL2CAPChannel::write(const std::vector& bytes) { + if (!this->channel) { + NIMBLE_LOGW(LOG_TAG, "L2CAP Channel not open"); + return false; + } + + struct ble_l2cap_chan_info info; + ble_l2cap_get_chan_info(channel, &info); + auto mtu = info.peer_coc_mtu < info.our_coc_mtu ? info.peer_coc_mtu : info.our_coc_mtu; + + auto start = bytes.begin(); + while (start != bytes.end()) { + auto end = start + mtu < bytes.end() ? start + mtu : bytes.end(); + if (writeFragment(start, end) < 0) { + return false; + } + start = end; + } + return true; +} + +// private +int NimBLEL2CAPChannel::handleConnectionEvent(struct ble_l2cap_event* event) { + channel = event->connect.chan; + struct ble_l2cap_chan_info info; + ble_l2cap_get_chan_info(channel, &info); + NIMBLE_LOGI(LOG_TAG, + "L2CAP COC 0x%04X connected. Local MTU = %d [%d], remote MTU = %d [%d].", + psm, + info.our_coc_mtu, + info.our_l2cap_mtu, + info.peer_coc_mtu, + info.peer_l2cap_mtu); + if (info.our_coc_mtu > info.peer_coc_mtu) { + NIMBLE_LOGW(LOG_TAG, "L2CAP COC 0x%04X connected, but local MTU is bigger than remote MTU.", psm); + } + auto mtu = info.peer_coc_mtu < info.our_coc_mtu ? info.peer_coc_mtu : info.our_coc_mtu; + callbacks->onConnect(this, mtu); + return 0; +} + +int NimBLEL2CAPChannel::handleAcceptEvent(struct ble_l2cap_event* event) { + NIMBLE_LOGI(LOG_TAG, "L2CAP COC 0x%04X accept.", psm); + if (!callbacks->shouldAcceptConnection(this)) { + NIMBLE_LOGI(LOG_TAG, "L2CAP COC 0x%04X refused by delegate.", psm); + return -1; + } + + struct os_mbuf* sdu_rx = os_mbuf_get_pkthdr(&_coc_mbuf_pool, 0); + assert(sdu_rx != NULL); + ble_l2cap_recv_ready(event->accept.chan, sdu_rx); + return 0; +} + +int NimBLEL2CAPChannel::handleDataReceivedEvent(struct ble_l2cap_event* event) { + NIMBLE_LOGD(LOG_TAG, "L2CAP COC 0x%04X data received.", psm); + + struct os_mbuf* rxd = event->receive.sdu_rx; + assert(rxd != NULL); + + int rx_len = (int)OS_MBUF_PKTLEN(rxd); + assert(rx_len <= (int)mtu); + + int res = os_mbuf_copydata(rxd, 0, rx_len, receiveBuffer); + assert(res == 0); + + NIMBLE_LOGD(LOG_TAG, "L2CAP COC 0x%04X received %d bytes.", psm, rx_len); + + res = os_mbuf_free_chain(rxd); + assert(res == 0); + + std::vector incomingData(receiveBuffer, receiveBuffer + rx_len); + callbacks->onRead(this, incomingData); + + struct os_mbuf* next = os_mbuf_get_pkthdr(&_coc_mbuf_pool, 0); + assert(next != NULL); + + res = ble_l2cap_recv_ready(channel, next); + assert(res == 0); + + return 0; +} + +int NimBLEL2CAPChannel::handleTxUnstalledEvent(struct ble_l2cap_event* event) { + if (m_pTaskData != nullptr) { + NimBLEUtils::taskRelease(*m_pTaskData, event->tx_unstalled.status); + } + + NIMBLE_LOGI(LOG_TAG, "L2CAP COC 0x%04X transmit unstalled.", psm); + return 0; +} + +int NimBLEL2CAPChannel::handleDisconnectionEvent(struct ble_l2cap_event* event) { + NIMBLE_LOGI(LOG_TAG, "L2CAP COC 0x%04X disconnected.", psm); + channel = NULL; + callbacks->onDisconnect(this); + return 0; +} + +/* STATIC */ +int NimBLEL2CAPChannel::handleL2capEvent(struct ble_l2cap_event* event, void* arg) { + NIMBLE_LOGD(LOG_TAG, "handleL2capEvent: handling l2cap event %d", event->type); + NimBLEL2CAPChannel* self = reinterpret_cast(arg); + + int returnValue = 0; + + switch (event->type) { + case BLE_L2CAP_EVENT_COC_CONNECTED: + returnValue = self->handleConnectionEvent(event); + break; + + case BLE_L2CAP_EVENT_COC_DISCONNECTED: + returnValue = self->handleDisconnectionEvent(event); + break; + + case BLE_L2CAP_EVENT_COC_ACCEPT: + returnValue = self->handleAcceptEvent(event); + break; + + case BLE_L2CAP_EVENT_COC_DATA_RECEIVED: + returnValue = self->handleDataReceivedEvent(event); + break; + + case BLE_L2CAP_EVENT_COC_TX_UNSTALLED: + returnValue = self->handleTxUnstalledEvent(event); + break; + + default: + NIMBLE_LOGW(LOG_TAG, "Unhandled l2cap event %d", event->type); + break; + } + + return returnValue; +} + +#endif // #if CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_L2CAP_COC_MAX_NUM diff --git a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEL2CAPChannel.h b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEL2CAPChannel.h new file mode 100644 index 000000000..41cd5a96e --- /dev/null +++ b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEL2CAPChannel.h @@ -0,0 +1,126 @@ +// +// (C) Dr. Michael 'Mickey' Lauer +// + +#ifndef NIMBLE_CPP_L2CAPCHANNEL_H_ +#define NIMBLE_CPP_L2CAPCHANNEL_H_ + +#include "nimconfig.h" +#if CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_L2CAP_COC_MAX_NUM + +# include "inttypes.h" +# if defined(CONFIG_NIMBLE_CPP_IDF) +# include "host/ble_l2cap.h" +# include "os/os_mbuf.h" +# else +# include "nimble/nimble/host/include/host/ble_l2cap.h" +# include "nimble/porting/nimble/include/os/os_mbuf.h" +# endif + +/**** FIX COMPILATION ****/ +# undef min +# undef max +/**************************/ + +# include +# include + +class NimBLEClient; +class NimBLEL2CAPChannelCallbacks; +struct NimBLETaskData; + +/** + * @brief Encapsulates a L2CAP channel. + * + * This class is used to encapsulate a L2CAP connection oriented channel, both + * from the "server" (which waits for the connection to be opened) and the "client" + * (which opens the connection) point of view. + */ +class NimBLEL2CAPChannel { + public: + /// @brief Open an L2CAP channel via the specified PSM and MTU. + /// @param[in] psm The PSM to use. + /// @param[in] mtu The MTU to use. Note that this is the local MTU. Upon opening the channel, + /// the final MTU will be negotiated to be the minimum of local and remote. + /// @param[in] callbacks The callbacks to use. NOTE that these callbacks are called from the + /// context of the NimBLE bluetooth task (`nimble_host`) and MUST be handled as fast as possible. + /// @return True if the channel was opened successfully, false otherwise. + static NimBLEL2CAPChannel* connect(NimBLEClient* client, uint16_t psm, uint16_t mtu, NimBLEL2CAPChannelCallbacks* callbacks); + + /// @brief Write data to the channel. + /// + /// If the size of the data exceeds the MTU, the data will be split into multiple fragments. + /// @return true on success, after the data has been sent. + /// @return false, if the data can't be sent. + /// + /// NOTE: This function will block until the data has been sent or an error occurred. + bool write(const std::vector& bytes); + + /// @return True, if the channel is connected. False, otherwise. + bool isConnected() const { return !!channel; } + + protected: + NimBLEL2CAPChannel(uint16_t psm, uint16_t mtu, NimBLEL2CAPChannelCallbacks* callbacks); + ~NimBLEL2CAPChannel(); + + int handleConnectionEvent(struct ble_l2cap_event* event); + int handleAcceptEvent(struct ble_l2cap_event* event); + int handleDataReceivedEvent(struct ble_l2cap_event* event); + int handleTxUnstalledEvent(struct ble_l2cap_event* event); + int handleDisconnectionEvent(struct ble_l2cap_event* event); + + private: + friend class NimBLEL2CAPServer; + static constexpr const char* LOG_TAG = "NimBLEL2CAPChannel"; + + const uint16_t psm; // PSM of the channel + const uint16_t mtu; // The requested (local) MTU of the channel, might be larger than negotiated MTU + struct ble_l2cap_chan* channel = nullptr; + NimBLEL2CAPChannelCallbacks* callbacks; + uint8_t* receiveBuffer = nullptr; // buffers a full (local) MTU + + // NimBLE memory pool + void* _coc_memory = nullptr; + struct os_mempool _coc_mempool; + struct os_mbuf_pool _coc_mbuf_pool; + + // Runtime handling + std::atomic stalled{false}; + NimBLETaskData* m_pTaskData{nullptr}; + + // Allocate / deallocate NimBLE memory pool + bool setupMemPool(); + void teardownMemPool(); + + // Writes data up to the size of the negotiated MTU to the channel. + int writeFragment(std::vector::const_iterator begin, std::vector::const_iterator end); + + // L2CAP event handler + static int handleL2capEvent(struct ble_l2cap_event* event, void* arg); +}; + +/** + * @brief Callbacks base class for the L2CAP channel. + */ +class NimBLEL2CAPChannelCallbacks { + public: + NimBLEL2CAPChannelCallbacks() = default; + virtual ~NimBLEL2CAPChannelCallbacks() = default; + + /// Called when the client attempts to open a channel on the server. + /// You can choose to accept or deny the connection. + /// Default implementation returns true. + virtual bool shouldAcceptConnection(NimBLEL2CAPChannel* channel) { return true; } + /// Called after a connection has been made. + /// Default implementation does nothing. + virtual void onConnect(NimBLEL2CAPChannel* channel, uint16_t negotiatedMTU) {}; + /// Called when data has been read from the channel. + /// Default implementation does nothing. + virtual void onRead(NimBLEL2CAPChannel* channel, std::vector& data) {}; + /// Called after the channel has been disconnected. + /// Default implementation does nothing. + virtual void onDisconnect(NimBLEL2CAPChannel* channel) {}; +}; + +#endif // CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_L2CAP_COC_MAX_NUM +#endif // NIMBLE_CPP_L2CAPCHANNEL_H_ diff --git a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEL2CAPServer.cpp b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEL2CAPServer.cpp new file mode 100644 index 000000000..c719694b2 --- /dev/null +++ b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEL2CAPServer.cpp @@ -0,0 +1,40 @@ +// +// (C) Dr. Michael 'Mickey' Lauer +// + +#include "NimBLEL2CAPServer.h" +#if CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_L2CAP_COC_MAX_NUM + +# include "NimBLEL2CAPChannel.h" +# include "NimBLEDevice.h" +# include "NimBLELog.h" + +static const char* LOG_TAG = "NimBLEL2CAPServer"; + +NimBLEL2CAPServer::NimBLEL2CAPServer() { + // Nothing to do here... +} + +NimBLEL2CAPServer::~NimBLEL2CAPServer() { + // Delete all services + for (auto service : this->services) { + delete service; + } +} + +NimBLEL2CAPChannel* NimBLEL2CAPServer::createService(const uint16_t psm, + const uint16_t mtu, + NimBLEL2CAPChannelCallbacks* callbacks) { + auto service = new NimBLEL2CAPChannel(psm, mtu, callbacks); + auto rc = ble_l2cap_create_server(psm, mtu, NimBLEL2CAPChannel::handleL2capEvent, service); + + if (rc != 0) { + NIMBLE_LOGE(LOG_TAG, "Could not ble_l2cap_create_server: %d", rc); + return nullptr; + } + + this->services.push_back(service); + return service; +} + +#endif // CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_L2CAP_COC_MAX_NUM diff --git a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEL2CAPServer.h b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEL2CAPServer.h new file mode 100644 index 000000000..405009a21 --- /dev/null +++ b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEL2CAPServer.h @@ -0,0 +1,41 @@ +// +// (C) Dr. Michael 'Mickey' Lauer +// + +#ifndef NIMBLE_CPP_L2CAPSERVER_H_ +#define NIMBLE_CPP_L2CAPSERVER_H_ +#include "nimconfig.h" +#if CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_L2CAP_COC_MAX_NUM + +# include "inttypes.h" +# include + +class NimBLEL2CAPChannel; +class NimBLEL2CAPChannelCallbacks; + +/** + * @brief L2CAP server class. + * + * Encapsulates a L2CAP server that can hold multiple services. Every service is represented by a channel object + * and an assorted set of callbacks. + */ +class NimBLEL2CAPServer { + public: + /// @brief Register a new L2CAP service instance. + /// @param psm The port multiplexor service number. + /// @param mtu The maximum transmission unit. + /// @param callbacks The callbacks for this service. + /// @return the newly created object, if the server registration was successful. + NimBLEL2CAPChannel* createService(const uint16_t psm, const uint16_t mtu, NimBLEL2CAPChannelCallbacks* callbacks); + + private: + NimBLEL2CAPServer(); + ~NimBLEL2CAPServer(); + std::vector services; + + friend class NimBLEL2CAPChannel; + friend class NimBLEDevice; +}; + +#endif // CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_L2CAP_COC_MAX_NUM +#endif // NIMBLE_CPP_L2CAPSERVER_H_ diff --git a/lib/libesp32_div/esp-nimble-cpp/src/NimBLELocalAttribute.h b/lib/libesp32_div/esp-nimble-cpp/src/NimBLELocalAttribute.h new file mode 100644 index 000000000..5427a9eab --- /dev/null +++ b/lib/libesp32_div/esp-nimble-cpp/src/NimBLELocalAttribute.h @@ -0,0 +1,58 @@ +/* + * Copyright 2020-2025 Ryan Powell 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_LOCAL_ATTRIBUTE_H_ +#define NIMBLE_CPP_LOCAL_ATTRIBUTE_H_ + +#include "nimconfig.h" +#if CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL + +# include "NimBLEAttribute.h" + +/** + * @brief A base class for local BLE attributes. + */ +class NimBLELocalAttribute : public NimBLEAttribute { + public: + /** + * @brief Get the removed flag. + * @return The removed flag. + */ + uint8_t getRemoved() const { return m_removed; } + + protected: + /** + * @brief Construct a local attribute. + */ + NimBLELocalAttribute(const NimBLEUUID& uuid, uint16_t handle) : NimBLEAttribute{uuid, handle}, m_removed{0} {} + + /** + * @brief Destroy the local attribute. + */ + ~NimBLELocalAttribute() = default; + + /** + * @brief Set the removed flag. + * @param [in] removed The removed flag. + */ + void setRemoved(uint8_t removed) { m_removed = removed; } + + uint8_t m_removed{0}; +}; + +#endif // CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL +#endif // NIMBLE_CPP_LOCAL_ATTRIBUTE_H_ diff --git a/lib/libesp32_div/esp-nimble-cpp/src/NimBLELocalValueAttribute.h b/lib/libesp32_div/esp-nimble-cpp/src/NimBLELocalValueAttribute.h new file mode 100644 index 000000000..c9f9d8deb --- /dev/null +++ b/lib/libesp32_div/esp-nimble-cpp/src/NimBLELocalValueAttribute.h @@ -0,0 +1,144 @@ +/* + * Copyright 2020-2025 Ryan Powell 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_LOCAL_VALUE_ATTRIBUTE_H_ +#define NIMBLE_LOCAL_VALUE_ATTRIBUTE_H_ + +#include "nimconfig.h" +#if CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL + +# if defined(CONFIG_NIMBLE_CPP_IDF) +# include "host/ble_hs.h" +# else +# include "nimble/nimble/host/include/host/ble_hs.h" +# endif + +/**** FIX COMPILATION ****/ +# undef min +# undef max +/**************************/ + +typedef enum { + READ = BLE_GATT_CHR_F_READ, + READ_ENC = BLE_GATT_CHR_F_READ_ENC, + READ_AUTHEN = BLE_GATT_CHR_F_READ_AUTHEN, + READ_AUTHOR = BLE_GATT_CHR_F_READ_AUTHOR, + WRITE = BLE_GATT_CHR_F_WRITE, + WRITE_NR = BLE_GATT_CHR_F_WRITE_NO_RSP, + WRITE_ENC = BLE_GATT_CHR_F_WRITE_ENC, + WRITE_AUTHEN = BLE_GATT_CHR_F_WRITE_AUTHEN, + WRITE_AUTHOR = BLE_GATT_CHR_F_WRITE_AUTHOR, + BROADCAST = BLE_GATT_CHR_F_BROADCAST, + NOTIFY = BLE_GATT_CHR_F_NOTIFY, + INDICATE = BLE_GATT_CHR_F_INDICATE +} NIMBLE_PROPERTY; + +# include "NimBLELocalAttribute.h" +# include "NimBLEValueAttribute.h" +# include "NimBLEAttValue.h" +# include +class NimBLEConnInfo; + +class NimBLELocalValueAttribute : public NimBLELocalAttribute, public NimBLEValueAttribute { + public: + /** + * @brief Get the properties of the attribute. + */ + uint16_t getProperties() const { return m_properties; } + + /** + * @brief Set the value of the attribute value. + * @param [in] data The data to set the value to. + * @param [in] size The size of the data. + */ + void setValue(const uint8_t* data, size_t size) { m_value.setValue(data, size); } + + /** + * @brief Set the value of the attribute value. + * @param [in] str The string to set the value to. + */ + void setValue(const char* str) { m_value.setValue(str); } + + /** + * @brief Set the value of the attribute value. + * @param [in] vec The vector to set the value to. + */ + void setValue(const std::vector& vec) { m_value.setValue(vec); } + + /** + * @brief Template to set the value to val. + * @param [in] val The value to set. + */ + template + void setValue(const T& val) { + m_value.setValue(val); + } + + protected: + friend class NimBLEServer; + + /** + * @brief Construct a new NimBLELocalValueAttribute object. + * @param [in] uuid The UUID of the attribute. + * @param [in] handle The handle of the attribute. + * @param [in] maxLen The maximum length of the attribute value. + * @param [in] initLen The initial length of the attribute value. + */ + NimBLELocalValueAttribute(const NimBLEUUID& uuid, + uint16_t handle, + uint16_t maxLen, + uint16_t initLen = CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH) + : NimBLELocalAttribute(uuid, handle), NimBLEValueAttribute(maxLen, initLen) {} + /** + * @brief Destroy the NimBLELocalValueAttribute object. + */ + virtual ~NimBLELocalValueAttribute() = default; + + /** + * @brief Callback function to support a read request. + * @param [in] connInfo A reference to a NimBLEConnInfo instance containing the peer info. + * @details This function is called by NimBLEServer when a read request is received. + */ + virtual void readEvent(NimBLEConnInfo& connInfo) = 0; + + /** + * @brief Callback function to support a write request. + * @param [in] val The value to write. + * @param [in] len The length of the value. + * @param [in] connInfo A reference to a NimBLEConnInfo instance containing the peer info. + * @details This function is called by NimBLEServer when a write request is received. + */ + virtual void writeEvent(const uint8_t* val, uint16_t len, NimBLEConnInfo& connInfo) = 0; + + /** + * @brief Get a pointer to value of the attribute. + * @return A pointer to the value of the attribute. + * @details This function is used by NimBLEServer when handling read/write requests. + */ + const NimBLEAttValue& getAttVal() const { return m_value; } + + /** + * @brief Set the properties of the attribute. + * @param [in] properties The properties of the attribute. + */ + void setProperties(uint16_t properties) { m_properties = properties; } + + uint16_t m_properties{0}; +}; + +#endif // CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL +#endif // NIMBLE_LOCAL_VALUE_ATTRIBUTE_H_ diff --git a/lib/libesp32_div/esp-nimble-cpp/src/NimBLELog.h b/lib/libesp32_div/esp-nimble-cpp/src/NimBLELog.h index 686c71cd1..a6c2e362b 100644 --- a/lib/libesp32_div/esp-nimble-cpp/src/NimBLELog.h +++ b/lib/libesp32_div/esp-nimble-cpp/src/NimBLELog.h @@ -1,79 +1,183 @@ /* - * NimBLELog.h + * Copyright 2020-2025 Ryan Powell and + * esp-nimble-cpp, NimBLE-Arduino contributors. * - * Created: on Feb 24 2020 - * Author H2zero + * 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 MAIN_NIMBLELOG_H_ -#define MAIN_NIMBLELOG_H_ + +#ifndef NIMBLE_CPP_LOG_H_ +#define NIMBLE_CPP_LOG_H_ #include "nimconfig.h" +#if CONFIG_BT_ENABLED -#if defined(CONFIG_BT_ENABLED) - -#if defined(CONFIG_NIMBLE_CPP_IDF) // using esp-idf +# if defined(CONFIG_NIMBLE_CPP_IDF) # include "esp_log.h" # include "console/console.h" # ifndef CONFIG_NIMBLE_CPP_LOG_LEVEL -# define CONFIG_NIMBLE_CPP_LOG_LEVEL 0 +# define CONFIG_NIMBLE_CPP_LOG_LEVEL 0 # endif -# define NIMBLE_CPP_LOG_PRINT(level, tag, format, ...) do { \ - if (CONFIG_NIMBLE_CPP_LOG_LEVEL >= level) \ - ESP_LOG_LEVEL_LOCAL(level, tag, format, ##__VA_ARGS__); \ - } while(0) +# if defined(CONFIG_NIMBLE_CPP_LOG_OVERRIDE_COLOR) +# if CONFIG_LOG_COLORS +# if defined(CONFIG_NIMBLE_CPP_LOG_OVERRIDE_COLOR_DEBUG_BLACK) +# define NIMBLE_CPP_LOG_COLOR_D LOG_COLOR(LOG_COLOR_BLACK) +# elif defined(CONFIG_NIMBLE_CPP_LOG_OVERRIDE_COLOR_DEBUG_RED) +# define NIMBLE_CPP_LOG_COLOR_D LOG_COLOR(LOG_COLOR_RED) +# elif defined(CONFIG_NIMBLE_CPP_LOG_OVERRIDE_COLOR_DEBUG_GREEN) +# define NIMBLE_CPP_LOG_COLOR_D LOG_COLOR(LOG_COLOR_GREEN) +# elif defined(CONFIG_NIMBLE_CPP_LOG_OVERRIDE_COLOR_DEBUG_YELLOW) +# define NIMBLE_CPP_LOG_COLOR_D LOG_COLOR(LOG_COLOR_BROWN) +# elif defined(CONFIG_NIMBLE_CPP_LOG_OVERRIDE_COLOR_DEBUG_BLUE) +# define NIMBLE_CPP_LOG_COLOR_D LOG_COLOR(LOG_COLOR_BLUE) +# elif defined(CONFIG_NIMBLE_CPP_LOG_OVERRIDE_COLOR_DEBUG_PURPLE) +# define NIMBLE_CPP_LOG_COLOR_D LOG_COLOR(LOG_COLOR_PURPLE) +# elif defined(CONFIG_NIMBLE_CPP_LOG_OVERRIDE_COLOR_DEBUG_CYAN) +# define NIMBLE_CPP_LOG_COLOR_D LOG_COLOR(LOG_COLOR_CYAN) +# else +# define NIMBLE_CPP_LOG_COLOR_D +# endif -# define NIMBLE_LOGD(tag, format, ...) \ - NIMBLE_CPP_LOG_PRINT(ESP_LOG_DEBUG, tag, format, ##__VA_ARGS__) +# if defined(CONFIG_NIMBLE_CPP_LOG_OVERRIDE_COLOR_INFO_BLACK) +# define NIMBLE_CPP_LOG_COLOR_I LOG_COLOR(LOG_COLOR_BLACK) +# elif defined(CONFIG_NIMBLE_CPP_LOG_OVERRIDE_COLOR_INFO_RED) +# define NIMBLE_CPP_LOG_COLOR_I LOG_COLOR(LOG_COLOR_RED) +# elif defined(CONFIG_NIMBLE_CPP_LOG_OVERRIDE_COLOR_INFO_GREEN) +# define NIMBLE_CPP_LOG_COLOR_I LOG_COLOR(LOG_COLOR_GREEN) +# elif defined(CONFIG_NIMBLE_CPP_LOG_OVERRIDE_COLOR_INFO_YELLOW) +# define NIMBLE_CPP_LOG_COLOR_I LOG_COLOR(LOG_COLOR_BROWN) +# elif defined(CONFIG_NIMBLE_CPP_LOG_OVERRIDE_COLOR_INFO_BLUE) +# define NIMBLE_CPP_LOG_COLOR_I LOG_COLOR(LOG_COLOR_BLUE) +# elif defined(CONFIG_NIMBLE_CPP_LOG_OVERRIDE_COLOR_INFO_PURPLE) +# define NIMBLE_CPP_LOG_COLOR_I LOG_COLOR(LOG_COLOR_PURPLE) +# elif defined(CONFIG_NIMBLE_CPP_LOG_OVERRIDE_COLOR_INFO_CYAN) +# define NIMBLE_CPP_LOG_COLOR_I LOG_COLOR(LOG_COLOR_CYAN) +# else +# define NIMBLE_CPP_LOG_COLOR_I +# endif -# define NIMBLE_LOGI(tag, format, ...) \ - NIMBLE_CPP_LOG_PRINT(ESP_LOG_INFO, tag, format, ##__VA_ARGS__) +# if defined(CONFIG_NIMBLE_CPP_LOG_OVERRIDE_COLOR_WARN_BLACK) +# define NIMBLE_CPP_LOG_COLOR_W LOG_COLOR(LOG_COLOR_BLACK) +# elif defined(CONFIG_NIMBLE_CPP_LOG_OVERRIDE_COLOR_WARN_RED) +# define NIMBLE_CPP_LOG_COLOR_W LOG_COLOR(LOG_COLOR_RED) +# elif defined(CONFIG_NIMBLE_CPP_LOG_OVERRIDE_COLOR_WARN_GREEN) +# define NIMBLE_CPP_LOG_COLOR_W LOG_COLOR(LOG_COLOR_GREEN) +# elif defined(CONFIG_NIMBLE_CPP_LOG_OVERRIDE_COLOR_WARN_YELLOW) +# define NIMBLE_CPP_LOG_COLOR_W LOG_COLOR(LOG_COLOR_BROWN) +# elif defined(CONFIG_NIMBLE_CPP_LOG_OVERRIDE_COLOR_WARN_BLUE) +# define NIMBLE_CPP_LOG_COLOR_W LOG_COLOR(LOG_COLOR_BLUE) +# elif defined(CONFIG_NIMBLE_CPP_LOG_OVERRIDE_COLOR_WARN_PURPLE) +# define NIMBLE_CPP_LOG_COLOR_W LOG_COLOR(LOG_COLOR_PURPLE) +# elif defined(CONFIG_NIMBLE_CPP_LOG_OVERRIDE_COLOR_WARN_CYAN) +# define NIMBLE_CPP_LOG_COLOR_W LOG_COLOR(LOG_COLOR_CYAN) +# else +# define NIMBLE_CPP_LOG_COLOR_W +# endif -# define NIMBLE_LOGW(tag, format, ...) \ - NIMBLE_CPP_LOG_PRINT(ESP_LOG_WARN, tag, format, ##__VA_ARGS__) +# if defined(CONFIG_NIMBLE_CPP_LOG_OVERRIDE_COLOR_ERR_BLACK) +# define NIMBLE_CPP_LOG_COLOR_E LOG_COLOR(LOG_COLOR_BLACK) +# elif defined(CONFIG_NIMBLE_CPP_LOG_OVERRIDE_COLOR_ERR_RED) +# define NIMBLE_CPP_LOG_COLOR_E LOG_COLOR(LOG_COLOR_RED) +# elif defined(CONFIG_NIMBLE_CPP_LOG_OVERRIDE_COLOR_ERR_GREEN) +# define NIMBLE_CPP_LOG_COLOR_E LOG_COLOR(LOG_COLOR_GREEN) +# elif defined(CONFIG_NIMBLE_CPP_LOG_OVERRIDE_COLOR_ERR_YELLOW) +# define NIMBLE_CPP_LOG_COLOR_E LOG_COLOR(LOG_COLOR_BROWN) +# elif defined(CONFIG_NIMBLE_CPP_LOG_OVERRIDE_COLOR_ERR_BLUE) +# define NIMBLE_CPP_LOG_COLOR_E LOG_COLOR(LOG_COLOR_BLUE) +# elif defined(CONFIG_NIMBLE_CPP_LOG_OVERRIDE_COLOR_ERR_PURPLE) +# define NIMBLE_CPP_LOG_COLOR_E LOG_COLOR(LOG_COLOR_PURPLE) +# elif defined(CONFIG_NIMBLE_CPP_LOG_OVERRIDE_COLOR_ERR_CYAN) +# define NIMBLE_CPP_LOG_COLOR_E LOG_COLOR(LOG_COLOR_CYAN) +# else +# define NIMBLE_CPP_LOG_COLOR_E +# endif +# else //CONFIG_LOG_COLORS +# define NIMBLE_CPP_LOG_COLOR_D +# define NIMBLE_CPP_LOG_COLOR_I +# define NIMBLE_CPP_LOG_COLOR_W +# define NIMBLE_CPP_LOG_COLOR_E +# endif //CONFIG_LOG_COLORS -# define NIMBLE_LOGE(tag, format, ...) \ - NIMBLE_CPP_LOG_PRINT(ESP_LOG_ERROR, tag, format, ##__VA_ARGS__) +# define NIMBLE_CPP_LOG_FORMAT(letter, format) NIMBLE_CPP_LOG_COLOR_##letter #letter " (%lu) %s: " format LOG_RESET_COLOR "\n" -#else // using Arduino +# define NIMBLE_CPP_LOG_LEVEL_LOCAL(level, tag, format, ...) \ + do { \ + if (level==ESP_LOG_ERROR) { esp_log_write(ESP_LOG_ERROR, tag, NIMBLE_CPP_LOG_FORMAT(E, format), esp_log_timestamp(), tag __VA_OPT__(,) __VA_ARGS__); } \ + else if (level==ESP_LOG_WARN) { esp_log_write(ESP_LOG_WARN, tag, NIMBLE_CPP_LOG_FORMAT(W, format), esp_log_timestamp(), tag __VA_OPT__(,) __VA_ARGS__); } \ + else if (level==ESP_LOG_INFO) { esp_log_write(ESP_LOG_INFO, tag, NIMBLE_CPP_LOG_FORMAT(I, format), esp_log_timestamp(), tag __VA_OPT__(,) __VA_ARGS__); } \ + else { esp_log_write(ESP_LOG_DEBUG, tag, NIMBLE_CPP_LOG_FORMAT(D, format), esp_log_timestamp(), tag __VA_OPT__(,) __VA_ARGS__); } \ + } while(0) + +# define NIMBLE_CPP_LOG_PRINT(level, tag, format, ...) \ + do { \ + if (CONFIG_NIMBLE_CPP_LOG_LEVEL >= level) NIMBLE_CPP_LOG_LEVEL_LOCAL(level, tag, format, ##__VA_ARGS__); \ + } while (0) + +# else +# define NIMBLE_CPP_LOG_PRINT(level, tag, format, ...) \ + do { \ + if (CONFIG_NIMBLE_CPP_LOG_LEVEL >= level) ESP_LOG_LEVEL_LOCAL(level, tag, format, ##__VA_ARGS__); \ + } while (0) + +# endif /* CONFIG_NIMBLE_CPP_LOG_OVERRIDE_COLOR */ + +# define NIMBLE_LOGD(tag, format, ...) NIMBLE_CPP_LOG_PRINT(ESP_LOG_DEBUG, tag, format, ##__VA_ARGS__) +# define NIMBLE_LOGI(tag, format, ...) NIMBLE_CPP_LOG_PRINT(ESP_LOG_INFO, tag, format, ##__VA_ARGS__) +# define NIMBLE_LOGW(tag, format, ...) NIMBLE_CPP_LOG_PRINT(ESP_LOG_WARN, tag, format, ##__VA_ARGS__) +# define NIMBLE_LOGE(tag, format, ...) NIMBLE_CPP_LOG_PRINT(ESP_LOG_ERROR, tag, format, ##__VA_ARGS__) + +# else # include "nimble/porting/nimble/include/syscfg/syscfg.h" # include "nimble/console/console.h" # ifndef CONFIG_NIMBLE_CPP_LOG_LEVEL -# if defined(ARDUINO_ARCH_ESP32) && defined(CORE_DEBUG_LEVEL) -# define CONFIG_NIMBLE_CPP_LOG_LEVEL CORE_DEBUG_LEVEL -# else -# define CONFIG_NIMBLE_CPP_LOG_LEVEL 0 -# endif +# if defined(ARDUINO_ARCH_ESP32) && defined(CORE_DEBUG_LEVEL) +# define CONFIG_NIMBLE_CPP_LOG_LEVEL CORE_DEBUG_LEVEL +# else +# define CONFIG_NIMBLE_CPP_LOG_LEVEL 0 +# endif # endif # if CONFIG_NIMBLE_CPP_LOG_LEVEL >= 4 -# define NIMBLE_LOGD( tag, format, ... ) console_printf("D %s: " format "\n", tag, ##__VA_ARGS__) +# define NIMBLE_LOGD(tag, format, ...) console_printf("D %s: " format "\n", tag, ##__VA_ARGS__) # else -# define NIMBLE_LOGD( tag, format, ... ) (void)tag +# define NIMBLE_LOGD(tag, format, ...) (void)tag # endif # if CONFIG_NIMBLE_CPP_LOG_LEVEL >= 3 -# define NIMBLE_LOGI( tag, format, ... ) console_printf("I %s: " format "\n", tag, ##__VA_ARGS__) +# define NIMBLE_LOGI(tag, format, ...) console_printf("I %s: " format "\n", tag, ##__VA_ARGS__) # else -# define NIMBLE_LOGI( tag, format, ... ) (void)tag +# define NIMBLE_LOGI(tag, format, ...) (void)tag # endif # if CONFIG_NIMBLE_CPP_LOG_LEVEL >= 2 -# define NIMBLE_LOGW( tag, format, ... ) console_printf("W %s: " format "\n", tag, ##__VA_ARGS__) +# define NIMBLE_LOGW(tag, format, ...) console_printf("W %s: " format "\n", tag, ##__VA_ARGS__) # else -# define NIMBLE_LOGW( tag, format, ... ) (void)tag +# define NIMBLE_LOGW(tag, format, ...) (void)tag # endif # if CONFIG_NIMBLE_CPP_LOG_LEVEL >= 1 -# define NIMBLE_LOGE( tag, format, ... ) console_printf("E %s: " format "\n", tag, ##__VA_ARGS__) +# define NIMBLE_LOGE(tag, format, ...) console_printf("E %s: " format "\n", tag, ##__VA_ARGS__) # else -# define NIMBLE_LOGE( tag, format, ... ) (void)tag +# define NIMBLE_LOGE(tag, format, ...) (void)tag # endif -#endif /* CONFIG_NIMBLE_CPP_IDF */ +# endif /* CONFIG_NIMBLE_CPP_IDF */ -#define NIMBLE_LOGC( tag, format, ... ) console_printf("CRIT %s: " format "\n", tag, ##__VA_ARGS__) +# define NIMBLE_LOGD_IF(cond, tag, format, ...) { if (cond) { NIMBLE_LOGD(tag, format, ##__VA_ARGS__); }} +# define NIMBLE_LOGI_IF(cond, tag, format, ...) { if (cond) { NIMBLE_LOGI(tag, format, ##__VA_ARGS__); }} +# define NIMBLE_LOGW_IF(cond, tag, format, ...) { if (cond) { NIMBLE_LOGW(tag, format, ##__VA_ARGS__); }} +# define NIMBLE_LOGE_IF(cond, tag, format, ...) { if (cond) { NIMBLE_LOGE(tag, format, ##__VA_ARGS__); }} +# define NIMBLE_LOGE_RC(rc, tag, format, ...) { if (rc) { NIMBLE_LOGE(tag, format "; rc=%d %s", ##__VA_ARGS__, rc, NimBLEUtils::returnCodeToString(rc)); }} // The LOG_LEVEL macros are used to set the log level for the NimBLE stack, but they pollute the global namespace and would override the loglevel enum of Tasmota. // So we undefine them here to avoid conflicts. @@ -98,5 +202,5 @@ #undef LOG_LEVEL_ERROR #endif -#endif /* CONFIG_BT_ENABLED */ -#endif /* MAIN_NIMBLELOG_H_ */ +#endif /* CONFIG_BT_ENABLED */ +#endif /* NIMBLE_CPP_LOG_H_ */ \ No newline at end of file diff --git a/lib/libesp32_div/esp-nimble-cpp/src/NimBLERemoteCharacteristic.cpp b/lib/libesp32_div/esp-nimble-cpp/src/NimBLERemoteCharacteristic.cpp index c2050ed8f..41029cbff 100644 --- a/lib/libesp32_div/esp-nimble-cpp/src/NimBLERemoteCharacteristic.cpp +++ b/lib/libesp32_div/esp-nimble-cpp/src/NimBLERemoteCharacteristic.cpp @@ -1,32 +1,43 @@ /* - * NimBLERemoteCharacteristic.cpp + * Copyright 2020-2025 Ryan Powell and + * esp-nimble-cpp, NimBLE-Arduino contributors. * - * Created: on Jan 27 2020 - * Author H2zero + * 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 * - * Originally: + * http://www.apache.org/licenses/LICENSE-2.0 * - * BLERemoteCharacteristic.cpp - * - * Created on: Mar 16, 2017 - * Author: kolban + * 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 "nimconfig.h" -#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL) - #include "NimBLERemoteCharacteristic.h" -#include "NimBLEUtils.h" -#include "NimBLELog.h" +#if CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL -#include +# include "NimBLERemoteDescriptor.h" +# include "NimBLERemoteService.h" +# include "NimBLEClient.h" +# include "NimBLEUtils.h" +# include "NimBLELog.h" + +# include + +struct NimBLEDescriptorFilter { + NimBLERemoteDescriptor* dsc; + const NimBLEUUID* uuid; + void* taskData; +}; static const char* LOG_TAG = "NimBLERemoteCharacteristic"; /** * @brief Constructor. - * @param [in] reference to the service this characteristic belongs to. - * @param [in] ble_gatt_chr struct defined as: + * @param [in] svc A pointer to the service this characteristic belongs to. + * @param [in] chr struct defined as: * struct ble_gatt_chr { * uint16_t def_handle; * uint16_t val_handle; @@ -34,34 +45,12 @@ static const char* LOG_TAG = "NimBLERemoteCharacteristic"; * ble_uuid_any_t uuid; * }; */ - NimBLERemoteCharacteristic::NimBLERemoteCharacteristic(NimBLERemoteService *pRemoteService, - const struct ble_gatt_chr *chr) -{ - NIMBLE_LOGD(LOG_TAG, ">> NimBLERemoteCharacteristic()"); - switch (chr->uuid.u.type) { - case BLE_UUID_TYPE_16: - m_uuid = NimBLEUUID(chr->uuid.u16.value); - break; - case BLE_UUID_TYPE_32: - m_uuid = NimBLEUUID(chr->uuid.u32.value); - break; - case BLE_UUID_TYPE_128: - m_uuid = NimBLEUUID(const_cast(&chr->uuid.u128)); - break; - default: - break; - } - - m_handle = chr->val_handle; - m_defHandle = chr->def_handle; - m_endHandle = 0; - m_charProp = chr->properties; - m_pRemoteService = pRemoteService; - m_notifyCallback = nullptr; - - NIMBLE_LOGD(LOG_TAG, "<< NimBLERemoteCharacteristic(): %s", m_uuid.toString().c_str()); - } // NimBLERemoteCharacteristic - +NimBLERemoteCharacteristic::NimBLERemoteCharacteristic(const NimBLERemoteService* svc, const ble_gatt_chr* chr) + : NimBLERemoteValueAttribute{chr->uuid, chr->val_handle}, + m_pRemoteService{svc}, + m_properties{chr->properties}, + m_notifyCallback{}, + m_vDescriptors{} {} // NimBLERemoteCharacteristic /** *@brief Destructor. @@ -70,298 +59,120 @@ NimBLERemoteCharacteristic::~NimBLERemoteCharacteristic() { deleteDescriptors(); } // ~NimBLERemoteCharacteristic -/* -#define BLE_GATT_CHR_PROP_BROADCAST 0x01 -#define BLE_GATT_CHR_PROP_READ 0x02 -#define BLE_GATT_CHR_PROP_WRITE_NO_RSP 0x04 -#define BLE_GATT_CHR_PROP_WRITE 0x08 -#define BLE_GATT_CHR_PROP_NOTIFY 0x10 -#define BLE_GATT_CHR_PROP_INDICATE 0x20 -#define BLE_GATT_CHR_PROP_AUTH_SIGN_WRITE 0x40 -#define BLE_GATT_CHR_PROP_EXTENDED 0x80 -*/ - -/** - * @brief Does the characteristic support broadcasting? - * @return True if the characteristic supports broadcasting. - */ -bool NimBLERemoteCharacteristic::canBroadcast() { - return (m_charProp & BLE_GATT_CHR_PROP_BROADCAST) != 0; -} // canBroadcast - - -/** - * @brief Does the characteristic support indications? - * @return True if the characteristic supports indications. - */ -bool NimBLERemoteCharacteristic::canIndicate() { - return (m_charProp & BLE_GATT_CHR_PROP_INDICATE) != 0; -} // canIndicate - - -/** - * @brief Does the characteristic support notifications? - * @return True if the characteristic supports notifications. - */ -bool NimBLERemoteCharacteristic::canNotify() { - return (m_charProp & BLE_GATT_CHR_PROP_NOTIFY) != 0; -} // canNotify - - -/** - * @brief Does the characteristic support reading? - * @return True if the characteristic supports reading. - */ -bool NimBLERemoteCharacteristic::canRead() { - return (m_charProp & BLE_GATT_CHR_PROP_READ) != 0; -} // canRead - - -/** - * @brief Does the characteristic support writing? - * @return True if the characteristic supports writing. - */ -bool NimBLERemoteCharacteristic::canWrite() { - return (m_charProp & BLE_GATT_CHR_PROP_WRITE) != 0; -} // canWrite - - -/** - * @brief Does the characteristic support writing with no response? - * @return True if the characteristic supports writing with no response. - */ -bool NimBLERemoteCharacteristic::canWriteNoResponse() { - return (m_charProp & BLE_GATT_CHR_PROP_WRITE_NO_RSP) != 0; -} // canWriteNoResponse - -/** - * @brief Return properties as bitfield - * - * @return uint8_t - */ -uint8_t NimBLERemoteCharacteristic::getProperties() { - return m_charProp; -} - - /** * @brief Callback used by the API when a descriptor is discovered or search complete. */ -int NimBLERemoteCharacteristic::descriptorDiscCB(uint16_t conn_handle, - const struct ble_gatt_error *error, - uint16_t chr_val_handle, - const struct ble_gatt_dsc *dsc, - void *arg) -{ - int rc = error->status; - NIMBLE_LOGD(LOG_TAG, "Descriptor Discovered >> status: %d handle: %d", - rc, (rc == 0) ? dsc->handle : -1); +int NimBLERemoteCharacteristic::descriptorDiscCB( + uint16_t connHandle, const ble_gatt_error* error, uint16_t chrHandle, const ble_gatt_dsc* dsc, void* arg) { + int rc = error->status; + auto filter = (NimBLEDescriptorFilter*)arg; + auto pTaskData = (NimBLETaskData*)filter->taskData; + const auto pChr = (NimBLERemoteCharacteristic*)pTaskData->m_pInstance; + const auto uuid = filter->uuid; // UUID to filter for + NIMBLE_LOGD(LOG_TAG, "Descriptor Discovery >> status: %d handle: %d", rc, (rc == 0) ? dsc->handle : -1); - desc_filter_t *filter = (desc_filter_t*)arg; - const NimBLEUUID *uuid_filter = filter->uuid; - ble_task_data_t *pTaskData = (ble_task_data_t*)filter->task_data; - NimBLERemoteCharacteristic *characteristic = (NimBLERemoteCharacteristic*)pTaskData->pATT; - - if (characteristic->getRemoteService()->getClient()->getConnId() != conn_handle){ - return 0; + // Results for chrHandle added until rc != 0 + // Must find specified UUID if filter is used + if (rc == 0 && pChr->getHandle() == chrHandle && (!uuid || 0 == ble_uuid_cmp(uuid->getBase(), &dsc->uuid.u))) { + // Return BLE_HS_EDONE if the descriptor was found, stop the search + pChr->m_vDescriptors.push_back(new NimBLERemoteDescriptor(pChr, dsc)); + rc = !!uuid * BLE_HS_EDONE; } - switch (rc) { - case 0: { - if (uuid_filter != nullptr) { - if (ble_uuid_cmp(&uuid_filter->getNative()->u, &dsc->uuid.u) != 0) { - return 0; - } else { - rc = BLE_HS_EDONE; - } - } - - NimBLERemoteDescriptor* pNewRemoteDescriptor = new NimBLERemoteDescriptor(characteristic, dsc); - characteristic->m_descriptorVector.push_back(pNewRemoteDescriptor); - break; - } - default: - break; + if (rc != 0) { + NimBLEUtils::taskRelease(*pTaskData, rc); + NIMBLE_LOGD(LOG_TAG, "<< Descriptor Discovery"); } - - /* If rc == BLE_HS_EDONE, resume the task with a success error code and stop the discovery process. - * Else if rc == 0, just return 0 to continue the discovery until we get BLE_HS_EDONE. - * If we get any other error code tell the application to abort by returning non-zero in the rc. - */ - if (rc == BLE_HS_EDONE) { - pTaskData->rc = 0; - xTaskNotifyGive(pTaskData->task); - } else if(rc != 0) { - // Error; abort discovery. - pTaskData->rc = rc; - xTaskNotifyGive(pTaskData->task); - } - - NIMBLE_LOGD(LOG_TAG,"<< Descriptor Discovered. status: %d", pTaskData->rc); return rc; } - -/** - * @brief callback from NimBLE when the next characteristic of the service is discovered. - */ -int NimBLERemoteCharacteristic::nextCharCB(uint16_t conn_handle, - const struct ble_gatt_error *error, - const struct ble_gatt_chr *chr, void *arg) -{ - int rc = error->status; - NIMBLE_LOGD(LOG_TAG, "Next Characteristic >> status: %d handle: %d", - rc, (rc == 0) ? chr->val_handle : -1); - - ble_task_data_t *pTaskData = (ble_task_data_t*)arg; - NimBLERemoteCharacteristic *pChar = (NimBLERemoteCharacteristic*)pTaskData->pATT; - - if (pChar->getRemoteService()->getClient()->getConnId() != conn_handle) { - return 0; - } - - if (rc == 0) { - pChar->m_endHandle = chr->def_handle - 1; - rc = BLE_HS_EDONE; - } else if (rc == BLE_HS_EDONE) { - pChar->m_endHandle = pChar->getRemoteService()->getEndHandle(); - } else { - pTaskData->rc = rc; - } - - xTaskNotifyGive(pTaskData->task); - return rc; -} - - /** * @brief Populate the descriptors (if any) for this characteristic. - * @param [in] the end handle of the characteristic, or the service, whichever comes first. + * @param [in] pFilter Pointer to a filter containing pointers to descriptor, UUID, and task data. + * @return True if successfully retrieved, success = BLE_HS_EDONE. */ -bool NimBLERemoteCharacteristic::retrieveDescriptors(const NimBLEUUID *uuid_filter) { +bool NimBLERemoteCharacteristic::retrieveDescriptors(NimBLEDescriptorFilter* pFilter) const { NIMBLE_LOGD(LOG_TAG, ">> retrieveDescriptors() for characteristic: %s", getUUID().toString().c_str()); // If this is the last handle then there are no descriptors - if (m_handle == getRemoteService()->getEndHandle()) { + if (getHandle() == getRemoteService()->getEndHandle()) { + NIMBLE_LOGD(LOG_TAG, "<< retrieveDescriptors(): found 0 descriptors."); return true; } - int rc = 0; - TaskHandle_t cur_task = xTaskGetCurrentTaskHandle(); - ble_task_data_t taskData = {this, cur_task, 0, nullptr}; + NimBLETaskData taskData(const_cast(this)); + NimBLEDescriptorFilter defaultFilter{nullptr, nullptr, &taskData}; + if (pFilter == nullptr) { + pFilter = &defaultFilter; + } - // If we don't know the end handle of this characteristic retrieve the next one in the service - // The end handle is the next characteristic definition handle -1. - if (m_endHandle == 0) { - rc = ble_gattc_disc_all_chrs(getRemoteService()->getClient()->getConnId(), - m_handle, + int rc = ble_gattc_disc_all_dscs(getClient()->getConnHandle(), + getHandle(), getRemoteService()->getEndHandle(), - NimBLERemoteCharacteristic::nextCharCB, - &taskData); - if (rc != 0) { - NIMBLE_LOGE(LOG_TAG, "Error getting end handle rc=%d", rc); - return false; - } - -#ifdef ulTaskNotifyValueClear - // Clear the task notification value to ensure we block - ulTaskNotifyValueClear(cur_task, ULONG_MAX); -#endif - ulTaskNotifyTake(pdTRUE, portMAX_DELAY); - - if (taskData.rc != 0) { - NIMBLE_LOGE(LOG_TAG, "Could not retrieve end handle rc=%d", taskData.rc); - return false; - } - } - - if (m_handle == m_endHandle) { - return true; - } - - desc_filter_t filter = {uuid_filter, &taskData}; - - rc = ble_gattc_disc_all_dscs(getRemoteService()->getClient()->getConnId(), - m_handle, - m_endHandle, - NimBLERemoteCharacteristic::descriptorDiscCB, - &filter); - + NimBLERemoteCharacteristic::descriptorDiscCB, + pFilter); if (rc != 0) { NIMBLE_LOGE(LOG_TAG, "ble_gattc_disc_all_dscs: rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc)); return false; } -#ifdef ulTaskNotifyValueClear - // Clear the task notification value to ensure we block - ulTaskNotifyValueClear(cur_task, ULONG_MAX); -#endif - ulTaskNotifyTake(pdTRUE, portMAX_DELAY); - - if (taskData.rc != 0) { - NIMBLE_LOGE(LOG_TAG, "Failed to retrieve descriptors; startHandle:%d endHandle:%d taskData.rc=%d", - m_handle, m_endHandle, taskData.rc); + auto prevDscCount = m_vDescriptors.size(); + NimBLEUtils::taskWait(taskData, BLE_NPL_TIME_FOREVER); + rc = ((NimBLETaskData*)pFilter->taskData)->m_flags; + if (rc != BLE_HS_EDONE) { + NIMBLE_LOGE(LOG_TAG, "<< retrieveDescriptors(): failed: rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc)); + return false; } - NIMBLE_LOGD(LOG_TAG, "<< retrieveDescriptors(): Found %d descriptors.", m_descriptorVector.size()); - return (taskData.rc == 0); -} // retrieveDescriptors + if (m_vDescriptors.size() > prevDscCount) { + pFilter->dsc = m_vDescriptors.back(); + } + NIMBLE_LOGD(LOG_TAG, "<< retrieveDescriptors(): found %d descriptors.", m_vDescriptors.size() - prevDscCount); + return true; +} // retrieveDescriptors /** * @brief Get the descriptor instance with the given UUID that belongs to this characteristic. * @param [in] uuid The UUID of the descriptor to find. - * @return The Remote descriptor (if present) or null if not present. + * @return The Remote descriptor (if present) or nullptr if not present. */ -NimBLERemoteDescriptor* NimBLERemoteCharacteristic::getDescriptor(const NimBLEUUID &uuid) { +NimBLERemoteDescriptor* NimBLERemoteCharacteristic::getDescriptor(const NimBLEUUID& uuid) const { NIMBLE_LOGD(LOG_TAG, ">> getDescriptor: uuid: %s", uuid.toString().c_str()); + NimBLEUUID uuidTmp{uuid}; + NimBLETaskData taskData(const_cast(this)); + NimBLEDescriptorFilter filter{nullptr, &uuidTmp, &taskData}; - for(auto &it: m_descriptorVector) { - if(it->getUUID() == uuid) { - NIMBLE_LOGD(LOG_TAG, "<< getDescriptor: found the descriptor with uuid: %s", uuid.toString().c_str()); - return it; + for (const auto& dsc : m_vDescriptors) { + if (dsc->getUUID() == uuid) { + filter.dsc = dsc; + goto Done; } } - size_t prev_size = m_descriptorVector.size(); - if(retrieveDescriptors(&uuid)) { - if(m_descriptorVector.size() > prev_size) { - return m_descriptorVector.back(); - } - - // If the request was successful but 16/32 bit uuid not found - // try again with the 128 bit uuid. - if(uuid.bitSize() == BLE_UUID_TYPE_16 || - uuid.bitSize() == BLE_UUID_TYPE_32) - { - NimBLEUUID uuid128(uuid); - uuid128.to128(); - if(retrieveDescriptors(&uuid128)) { - if(m_descriptorVector.size() > prev_size) { - return m_descriptorVector.back(); - } - } - } else { - // If the request was successful but the 128 bit uuid not found - // try again with the 16 bit uuid. - NimBLEUUID uuid16(uuid); - uuid16.to16(); - // if the uuid was 128 bit but not of the BLE base type this check will fail - if (uuid16.bitSize() == BLE_UUID_TYPE_16) { - if(retrieveDescriptors(&uuid16)) { - if(m_descriptorVector.size() > prev_size) { - return m_descriptorVector.back(); - } - } - } - } + if (!retrieveDescriptors(&filter) || filter.dsc) { + goto Done; } - NIMBLE_LOGD(LOG_TAG, "<< getDescriptor: Not found"); - return nullptr; + // Try again with 128 bit uuid if request succeeded but no descriptor found. + if (uuid.bitSize() != BLE_UUID_TYPE_128) { + uuidTmp.to128(); + retrieveDescriptors(&filter); + goto Done; + } + + // If the uuid was 128 bit, try again with 16 bit uuid. + uuidTmp.to16(); + if (uuidTmp.bitSize() == BLE_UUID_TYPE_16) { + filter.uuid = &uuidTmp; + retrieveDescriptors(&filter); + } + +Done: + NIMBLE_LOGD(LOG_TAG, "<< getDescriptor: %sfound", filter.dsc ? "" : "not "); + return filter.dsc; } // getDescriptor - /** * @brief Get a pointer to the vector of found descriptors. * @param [in] refresh If true the current descriptor vector will be cleared and\n @@ -370,201 +181,39 @@ NimBLERemoteDescriptor* NimBLERemoteCharacteristic::getDescriptor(const NimBLEUU * of this characteristic. * @return A pointer to the vector of descriptors for this characteristic. */ -std::vector* NimBLERemoteCharacteristic::getDescriptors(bool refresh) { - if(refresh) { +const std::vector& NimBLERemoteCharacteristic::getDescriptors(bool refresh) const { + if (refresh) { deleteDescriptors(); - - if (!retrieveDescriptors()) { - NIMBLE_LOGE(LOG_TAG, "Error: Failed to get descriptors"); - } - else{ - NIMBLE_LOGI(LOG_TAG, "Found %d descriptor(s)", m_descriptorVector.size()); - } + retrieveDescriptors(); } - return &m_descriptorVector; -} // getDescriptors + return m_vDescriptors; +} // getDescriptors /** * @brief Get iterator to the beginning of the vector of remote descriptor pointers. * @return An iterator to the beginning of the vector of remote descriptor pointers. */ -std::vector::iterator NimBLERemoteCharacteristic::begin() { - return m_descriptorVector.begin(); +std::vector::iterator NimBLERemoteCharacteristic::begin() const { + return m_vDescriptors.begin(); } - /** * @brief Get iterator to the end of the vector of remote descriptor pointers. * @return An iterator to the end of the vector of remote descriptor pointers. */ -std::vector::iterator NimBLERemoteCharacteristic::end() { - return m_descriptorVector.end(); +std::vector::iterator NimBLERemoteCharacteristic::end() const { + return m_vDescriptors.end(); } - -/** - * @brief Get the handle for this characteristic. - * @return The handle for this characteristic. - */ -uint16_t NimBLERemoteCharacteristic::getHandle() { - return m_handle; -} // getHandle - -/** - * @brief Get the handle for this characteristics definition. - * @return The handle for this characteristic definition. - */ -uint16_t NimBLERemoteCharacteristic::getDefHandle() { - return m_defHandle; -} // getDefHandle - - /** * @brief Get the remote service associated with this characteristic. * @return The remote service associated with this characteristic. */ -NimBLERemoteService* NimBLERemoteCharacteristic::getRemoteService() { +const NimBLERemoteService* NimBLERemoteCharacteristic::getRemoteService() const { return m_pRemoteService; } // getRemoteService - -/** - * @brief Get the UUID for this characteristic. - * @return The UUID for this characteristic. - */ -NimBLEUUID NimBLERemoteCharacteristic::getUUID() { - return m_uuid; -} // getUUID - - -/** - * @brief Get the value of the remote characteristic. - * @param [in] timestamp A pointer to a time_t struct to store the time the value was read. - * @return The value of the remote characteristic. - */ -NimBLEAttValue NimBLERemoteCharacteristic::getValue(time_t *timestamp) { - if(timestamp != nullptr) { - *timestamp = m_value.getTimeStamp(); - } - - return m_value; -} // getValue - - -/** - * @brief Read the value of the remote characteristic. - * @param [in] timestamp A pointer to a time_t struct to store the time the value was read. - * @return The value of the remote characteristic. - */ -NimBLEAttValue NimBLERemoteCharacteristic::readValue(time_t *timestamp) { - NIMBLE_LOGD(LOG_TAG, ">> readValue(): uuid: %s, handle: %d 0x%.2x", - getUUID().toString().c_str(), getHandle(), getHandle()); - - NimBLEClient* pClient = getRemoteService()->getClient(); - NimBLEAttValue value; - - if (!pClient->isConnected()) { - NIMBLE_LOGE(LOG_TAG, "Disconnected"); - return value; - } - - int rc = 0; - int retryCount = 1; - TaskHandle_t cur_task = xTaskGetCurrentTaskHandle(); - ble_task_data_t taskData = {this, cur_task, 0, &value}; - - do { - rc = ble_gattc_read_long(pClient->getConnId(), m_handle, 0, - NimBLERemoteCharacteristic::onReadCB, - &taskData); - if (rc != 0) { - NIMBLE_LOGE(LOG_TAG, "Error: Failed to read characteristic; rc=%d, %s", - rc, NimBLEUtils::returnCodeToString(rc)); - return value; - } - -#ifdef ulTaskNotifyValueClear - // Clear the task notification value to ensure we block - ulTaskNotifyValueClear(cur_task, ULONG_MAX); -#endif - ulTaskNotifyTake(pdTRUE, portMAX_DELAY); - rc = taskData.rc; - - switch(rc){ - case 0: - case BLE_HS_EDONE: - rc = 0; - break; - // Characteristic is not long-readable, return with what we have. - case BLE_HS_ATT_ERR(BLE_ATT_ERR_ATTR_NOT_LONG): - NIMBLE_LOGI(LOG_TAG, "Attribute not long"); - rc = 0; - break; - case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_AUTHEN): - case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_AUTHOR): - case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_ENC): - if (retryCount && pClient->secureConnection()) - break; - /* Else falls through. */ - default: - NIMBLE_LOGE(LOG_TAG, "<< readValue rc=%d", rc); - return value; - } - } while(rc != 0 && retryCount--); - - value.setTimeStamp(); - m_value = value; - if(timestamp != nullptr) { - *timestamp = value.getTimeStamp(); - } - - NIMBLE_LOGD(LOG_TAG, "<< readValue length: %d rc=%d", value.length(), rc); - return value; -} // readValue - - -/** - * @brief Callback for characteristic read operation. - * @return success == 0 or error code. - */ -int NimBLERemoteCharacteristic::onReadCB(uint16_t conn_handle, - const struct ble_gatt_error *error, - struct ble_gatt_attr *attr, void *arg) -{ - ble_task_data_t *pTaskData = (ble_task_data_t*)arg; - NimBLERemoteCharacteristic *characteristic = (NimBLERemoteCharacteristic*)pTaskData->pATT; - uint16_t conn_id = characteristic->getRemoteService()->getClient()->getConnId(); - - if(conn_id != conn_handle) { - return 0; - } - - NIMBLE_LOGI(LOG_TAG, "Read complete; status=%d conn_handle=%d", error->status, conn_handle); - - NimBLEAttValue *valBuf = (NimBLEAttValue*)pTaskData->buf; - int rc = error->status; - - if(rc == 0) { - if(attr) { - uint16_t data_len = OS_MBUF_PKTLEN(attr->om); - if((valBuf->size() + data_len) > BLE_ATT_ATTR_MAX_LEN) { - rc = BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; - } else { - NIMBLE_LOGD(LOG_TAG, "Got %u bytes", data_len); - valBuf->append(attr->om->om_data, data_len); - return 0; - } - } - } - - pTaskData->rc = rc; - xTaskNotifyGive(pTaskData->task); - - return rc; -} // onReadCB - - /** * @brief Subscribe or unsubscribe for notifications or indications. * @param [in] val 0x00 to unsubscribe, 0x01 for notifications, 0x02 for indications. @@ -573,23 +222,20 @@ int NimBLERemoteCharacteristic::onReadCB(uint16_t conn_handle, * If NULL is provided then no callback is performed. * @return false if writing to the descriptor failed. */ -bool NimBLERemoteCharacteristic::setNotify(uint16_t val, notify_callback notifyCallback, bool response) { - NIMBLE_LOGD(LOG_TAG, ">> setNotify(): %s, %02x", toString().c_str(), val); - - m_notifyCallback = notifyCallback; +bool NimBLERemoteCharacteristic::setNotify(uint16_t val, notify_callback notifyCallback, bool response) const { + NIMBLE_LOGD(LOG_TAG, ">> setNotify()"); + m_notifyCallback = notifyCallback; NimBLERemoteDescriptor* desc = getDescriptor(NimBLEUUID((uint16_t)0x2902)); - if(desc == nullptr) { + if (desc == nullptr) { NIMBLE_LOGW(LOG_TAG, "<< setNotify(): Callback set, CCCD not found"); return true; } NIMBLE_LOGD(LOG_TAG, "<< setNotify()"); - - return desc->writeValue((uint8_t *)&val, 2, response); + return desc->writeValue(reinterpret_cast(&val), 2, response); } // setNotify - /** * @brief Subscribe for notifications or indications. * @param [in] notifications If true, subscribe for notifications, false subscribe for indications. @@ -598,71 +244,127 @@ bool NimBLERemoteCharacteristic::setNotify(uint16_t val, notify_callback notifyC * If NULL is provided then no callback is performed. * @return false if writing to the descriptor failed. */ -bool NimBLERemoteCharacteristic::subscribe(bool notifications, notify_callback notifyCallback, bool response) { - if(notifications) { - return setNotify(0x01, notifyCallback, response); - } else { - return setNotify(0x02, notifyCallback, response); - } +bool NimBLERemoteCharacteristic::subscribe(bool notifications, notify_callback notifyCallback, bool response) const { + return setNotify(notifications ? 0x01 : 0x02, notifyCallback, response); } // subscribe - /** * @brief Unsubscribe for notifications or indications. * @param [in] response bool if true, require a write response from the descriptor write operation. * @return false if writing to the descriptor failed. */ -bool NimBLERemoteCharacteristic::unsubscribe(bool response) { +bool NimBLERemoteCharacteristic::unsubscribe(bool response) const { return setNotify(0x00, nullptr, response); } // unsubscribe - /** * @brief Delete the descriptors in the descriptor vector. - * @details We maintain a vector called m_descriptorVector that contains pointers to NimBLERemoteDescriptors + * @details We maintain a vector called m_vDescriptors that contains pointers to NimBLERemoteDescriptors * object references. Since we allocated these in this class, we are also responsible for deleting * them. This method does just that. */ -void NimBLERemoteCharacteristic::deleteDescriptors() { +void NimBLERemoteCharacteristic::deleteDescriptors() const { NIMBLE_LOGD(LOG_TAG, ">> deleteDescriptors"); - for(auto &it: m_descriptorVector) { + for (const auto& it : m_vDescriptors) { delete it; } - m_descriptorVector.clear(); + std::vector().swap(m_vDescriptors); + NIMBLE_LOGD(LOG_TAG, "<< deleteDescriptors"); } // deleteDescriptors - /** * @brief Delete descriptor by UUID * @param [in] uuid The UUID of the descriptor to be deleted. * @return Number of descriptors left in the vector. */ -size_t NimBLERemoteCharacteristic::deleteDescriptor(const NimBLEUUID &uuid) { +size_t NimBLERemoteCharacteristic::deleteDescriptor(const NimBLEUUID& uuid) const { NIMBLE_LOGD(LOG_TAG, ">> deleteDescriptor"); - for(auto it = m_descriptorVector.begin(); it != m_descriptorVector.end(); ++it) { - if((*it)->getUUID() == uuid) { - delete *it; - m_descriptorVector.erase(it); + for (auto it = m_vDescriptors.begin(); it != m_vDescriptors.end(); ++it) { + if ((*it)->getUUID() == uuid) { + delete (*it); + m_vDescriptors.erase(it); break; } } NIMBLE_LOGD(LOG_TAG, "<< deleteDescriptor"); - - return m_descriptorVector.size(); + return m_vDescriptors.size(); } // deleteDescriptor +/** + * @brief Does the characteristic support value broadcasting? + * @return True if supported. + */ +bool NimBLERemoteCharacteristic::canBroadcast() const { + return (m_properties & BLE_GATT_CHR_PROP_BROADCAST); +}; + +/** + * @brief Does the characteristic support reading? + * @return True if supported. + */ +bool NimBLERemoteCharacteristic::canRead() const { + return (m_properties & BLE_GATT_CHR_PROP_READ); +}; + +/** + * @brief Does the characteristic support writing without a response? + * @return True if supported. + */ +bool NimBLERemoteCharacteristic::canWriteNoResponse() const { + return (m_properties & BLE_GATT_CHR_PROP_WRITE_NO_RSP); +}; + +/** + * @brief Does the characteristic support writing? + * @return True if supported. + */ +bool NimBLERemoteCharacteristic::canWrite() const { + return (m_properties & BLE_GATT_CHR_PROP_WRITE); +}; + +/** + * @brief Does the characteristic support reading with encryption? + * @return True if supported. + */ +bool NimBLERemoteCharacteristic::canNotify() const { + return (m_properties & BLE_GATT_CHR_PROP_NOTIFY); +}; + +/** + * @brief Does the characteristic support indication? + * @return True if supported. + */ +bool NimBLERemoteCharacteristic::canIndicate() const { + return (m_properties & BLE_GATT_CHR_PROP_INDICATE); +}; + +/** + * @brief Does the characteristic support signed writing? + * @return True if supported. + */ +bool NimBLERemoteCharacteristic::canWriteSigned() const { + return (m_properties & BLE_GATT_CHR_PROP_AUTH_SIGN_WRITE); +}; + +/** + * @brief Does the characteristic support extended properties? + * @return True if supported. + */ +bool NimBLERemoteCharacteristic::hasExtendedProps() const { + return (m_properties & BLE_GATT_CHR_PROP_EXTENDED); +}; /** * @brief Convert a NimBLERemoteCharacteristic to a string representation; * @return a String representation. */ -std::string NimBLERemoteCharacteristic::toString() { +std::string NimBLERemoteCharacteristic::toString() const { std::string res = "Characteristic: uuid: " + m_uuid.toString(); - char val[6]; + char val[6]; res += ", handle: "; snprintf(val, sizeof(val), "%d", getHandle()); res += val; @@ -671,145 +373,18 @@ std::string NimBLERemoteCharacteristic::toString() { res += val; res += ", props: "; res += " 0x"; - snprintf(val, sizeof(val), "%02x", m_charProp); + snprintf(val, sizeof(val), "%02x", m_properties); res += val; - for(auto &it: m_descriptorVector) { + for (const auto& it : m_vDescriptors) { res += "\n" + it->toString(); } return res; } // toString +NimBLEClient* NimBLERemoteCharacteristic::getClient() const { + return getRemoteService()->getClient(); +} // getClient -/** - * @brief Write a new value to the remote characteristic from a std::vector. - * @param [in] vec A std::vector value to write to the remote characteristic. - * @param [in] response Whether we require a response from the write. - * @return false if not connected or otherwise cannot perform write. - */ -bool NimBLERemoteCharacteristic::writeValue(const std::vector& vec, bool response) { - return writeValue((uint8_t*)&vec[0], vec.size(), response); -} // writeValue - - -/** - * @brief Write a new value to the remote characteristic from a const char*. - * @param [in] char_s A character string to write to the remote characteristic. - * @param [in] response Whether we require a response from the write. - * @return false if not connected or otherwise cannot perform write. - */ -bool NimBLERemoteCharacteristic::writeValue(const char* char_s, bool response) { - return writeValue((uint8_t*)char_s, strlen(char_s), response); -} // writeValue - - -/** - * @brief Write a new value to the remote characteristic from a data buffer. - * @param [in] data A pointer to a data buffer. - * @param [in] length The length of the data in the data buffer. - * @param [in] response Whether we require a response from the write. - * @return false if not connected or otherwise cannot perform write. - */ -bool NimBLERemoteCharacteristic::writeValue(const uint8_t* data, size_t length, bool response) { - - NIMBLE_LOGD(LOG_TAG, ">> writeValue(), length: %d", length); - - NimBLEClient* pClient = getRemoteService()->getClient(); - - if (!pClient->isConnected()) { - NIMBLE_LOGE(LOG_TAG, "Disconnected"); - return false; - } - - int rc = 0; - int retryCount = 1; - uint16_t mtu = ble_att_mtu(pClient->getConnId()) - 3; - - // Check if the data length is longer than we can write in one connection event. - // If so we must do a long write which requires a response. - if(length <= mtu && !response) { - rc = ble_gattc_write_no_rsp_flat(pClient->getConnId(), m_handle, data, length); - return (rc==0); - } - - TaskHandle_t cur_task = xTaskGetCurrentTaskHandle(); - ble_task_data_t taskData = {this, cur_task, 0, nullptr}; - - do { - if(length > mtu) { - NIMBLE_LOGI(LOG_TAG,"long write %d bytes", length); - os_mbuf *om = ble_hs_mbuf_from_flat(data, length); - rc = ble_gattc_write_long(pClient->getConnId(), m_handle, 0, om, - NimBLERemoteCharacteristic::onWriteCB, - &taskData); - } else { - rc = ble_gattc_write_flat(pClient->getConnId(), m_handle, - data, length, - NimBLERemoteCharacteristic::onWriteCB, - &taskData); - } - if (rc != 0) { - NIMBLE_LOGE(LOG_TAG, "Error: Failed to write characteristic; rc=%d", rc); - return false; - } - -#ifdef ulTaskNotifyValueClear - // Clear the task notification value to ensure we block - ulTaskNotifyValueClear(cur_task, ULONG_MAX); -#endif - ulTaskNotifyTake(pdTRUE, portMAX_DELAY); - rc = taskData.rc; - - switch(rc){ - case 0: - case BLE_HS_EDONE: - rc = 0; - break; - case BLE_HS_ATT_ERR(BLE_ATT_ERR_ATTR_NOT_LONG): - NIMBLE_LOGE(LOG_TAG, "Long write not supported by peer; Truncating length to %d", mtu); - retryCount++; - length = mtu; - break; - - case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_AUTHEN): - case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_AUTHOR): - case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_ENC): - if (retryCount && pClient->secureConnection()) - break; - /* Else falls through. */ - default: - NIMBLE_LOGE(LOG_TAG, "<< writeValue, rc: %d", rc); - return false; - } - } while(rc != 0 && retryCount--); - - NIMBLE_LOGD(LOG_TAG, "<< writeValue, rc: %d", rc); - return (rc == 0); -} // writeValue - - -/** - * @brief Callback for characteristic write operation. - * @return success == 0 or error code. - */ -int NimBLERemoteCharacteristic::onWriteCB(uint16_t conn_handle, - const struct ble_gatt_error *error, - struct ble_gatt_attr *attr, void *arg) -{ - ble_task_data_t *pTaskData = (ble_task_data_t*)arg; - NimBLERemoteCharacteristic *characteristic = (NimBLERemoteCharacteristic*)pTaskData->pATT; - - if(characteristic->getRemoteService()->getClient()->getConnId() != conn_handle){ - return 0; - } - - NIMBLE_LOGI(LOG_TAG, "Write complete; status=%d conn_handle=%d", error->status, conn_handle); - - pTaskData->rc = error->status; - xTaskNotifyGive(pTaskData->task); - - return 0; -} - -#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL */ +#endif // CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL diff --git a/lib/libesp32_div/esp-nimble-cpp/src/NimBLERemoteCharacteristic.h b/lib/libesp32_div/esp-nimble-cpp/src/NimBLERemoteCharacteristic.h index ea4616e51..474c8a3f3 100644 --- a/lib/libesp32_div/esp-nimble-cpp/src/NimBLERemoteCharacteristic.h +++ b/lib/libesp32_div/esp-nimble-cpp/src/NimBLERemoteCharacteristic.h @@ -1,181 +1,84 @@ /* - * NimBLERemoteCharacteristic.h + * Copyright 2020-2025 Ryan Powell and + * esp-nimble-cpp, NimBLE-Arduino contributors. * - * Created: on Jan 27 2020 - * Author H2zero + * 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 * - * Originally: + * http://www.apache.org/licenses/LICENSE-2.0 * - * BLERemoteCharacteristic.h - * - * Created on: Jul 8, 2017 - * Author: kolban + * 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 COMPONENTS_NIMBLEREMOTECHARACTERISTIC_H_ -#define COMPONENTS_NIMBLEREMOTECHARACTERISTIC_H_ +#ifndef NIMBLE_CPP_REMOTE_CHARACTERISTIC_H_ +#define NIMBLE_CPP_REMOTE_CHARACTERISTIC_H_ #include "nimconfig.h" -#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL) +#if CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL -#include "NimBLERemoteService.h" -#include "NimBLERemoteDescriptor.h" - -#include -#include -#include "NimBLELog.h" +# include "NimBLERemoteValueAttribute.h" +# include +# include +class NimBLEUUID; class NimBLERemoteService; class NimBLERemoteDescriptor; - - -typedef std::function notify_callback; - -typedef struct { - const NimBLEUUID *uuid; - void *task_data; -} desc_filter_t; - +struct NimBLEDescriptorFilter; /** - * @brief A model of a remote %BLE characteristic. + * @brief A model of a remote BLE characteristic. */ -class NimBLERemoteCharacteristic { -public: +class NimBLERemoteCharacteristic : public NimBLERemoteValueAttribute { + public: + std::string toString() const; + const NimBLERemoteService* getRemoteService() const; + void deleteDescriptors() const; + size_t deleteDescriptor(const NimBLEUUID& uuid) const; + bool canBroadcast() const; + bool canRead() const; + bool canWriteNoResponse() const; + bool canWrite() const; + bool canNotify() const; + bool canIndicate() const; + bool canWriteSigned() const; + bool hasExtendedProps() const; + NimBLEClient* getClient() const override; + uint8_t getProperties() const {return m_properties;}; + + typedef std::function notify_callback; + + bool subscribe(bool notifications = true, const notify_callback notifyCallback = nullptr, bool response = true) const; + bool unsubscribe(bool response = true) const; + + std::vector::iterator begin() const; + std::vector::iterator end() const; + NimBLERemoteDescriptor* getDescriptor(const NimBLEUUID& uuid) const; + const std::vector& getDescriptors(bool refresh = false) const; + + private: + friend class NimBLEClient; + friend class NimBLERemoteService; + + NimBLERemoteCharacteristic(const NimBLERemoteService* pRemoteService, const ble_gatt_chr* chr); ~NimBLERemoteCharacteristic(); - // Public member functions - bool canBroadcast(); - bool canIndicate(); - bool canNotify(); - bool canRead(); - bool canWrite(); - bool canWriteNoResponse(); - uint8_t getProperties(); - std::vector::iterator begin(); - std::vector::iterator end(); - NimBLERemoteDescriptor* getDescriptor(const NimBLEUUID &uuid); - std::vector* getDescriptors(bool refresh = false); - void deleteDescriptors(); - size_t deleteDescriptor(const NimBLEUUID &uuid); - uint16_t getHandle(); - uint16_t getDefHandle(); - NimBLEUUID getUUID(); - NimBLEAttValue readValue(time_t *timestamp = nullptr); - std::string toString(); - NimBLERemoteService* getRemoteService(); - NimBLEAttValue getValue(time_t *timestamp = nullptr); - bool subscribe(bool notifications = true, - notify_callback notifyCallback = nullptr, - bool response = true); - bool unsubscribe(bool response = true); - bool writeValue(const uint8_t* data, - size_t length, - bool response = false); - bool writeValue(const std::vector& v, bool response = false); - bool writeValue(const char* s, bool response = false); + bool setNotify(uint16_t val, notify_callback notifyCallback = nullptr, bool response = true) const; + bool retrieveDescriptors(NimBLEDescriptorFilter* pFilter = nullptr) const; + static int descriptorDiscCB( + uint16_t connHandle, const ble_gatt_error* error, uint16_t chrHandle, const ble_gatt_dsc* dsc, void* arg); - /*********************** Template Functions ************************/ + const NimBLERemoteService* m_pRemoteService{nullptr}; + uint8_t m_properties{0}; + mutable notify_callback m_notifyCallback{nullptr}; + mutable std::vector m_vDescriptors{}; - /** - * @brief Template to set the remote characteristic value to val. - * @param [in] s The value to write. - * @param [in] response True == request write response. - * @details Only used for non-arrays and types without a `c_str()` method. - */ - template -#ifdef _DOXYGEN_ - bool -#else - typename std::enable_if::value && !Has_c_str_len::value, bool>::type -#endif - writeValue(const T& s, bool response = false) { - return writeValue((uint8_t*)&s, sizeof(T), response); - } - - /** - * @brief Template to set the remote characteristic value to val. - * @param [in] s The value to write. - * @param [in] response True == request write response. - * @details Only used if the has a `c_str()` method. - */ - template -#ifdef _DOXYGEN_ - bool -#else - typename std::enable_if::value, bool>::type -#endif - writeValue(const T& s, bool response = false) { - return writeValue((uint8_t*)s.c_str(), s.length(), response); - } - - /** - * @brief Template to convert the remote characteristic data to . - * @tparam T The type to convert the data to. - * @param [in] timestamp A pointer to a time_t struct to store the time the value was read. - * @param [in] skipSizeCheck If true it will skip checking if the data size is less than sizeof(). - * @return The data converted to or NULL if skipSizeCheck is false and the data is - * less than sizeof(). - * @details Use: getValue(×tamp, skipSizeCheck); - */ - template - T getValue(time_t *timestamp = nullptr, bool skipSizeCheck = false) { - if(!skipSizeCheck && m_value.size() < sizeof(T)) return T(); - return *((T *)m_value.getValue(timestamp)); - } - - /** - * @brief Template to convert the remote characteristic data to . - * @tparam T The type to convert the data to. - * @param [in] timestamp A pointer to a time_t struct to store the time the value was read. - * @param [in] skipSizeCheck If true it will skip checking if the data size is less than sizeof(). - * @return The data converted to or NULL if skipSizeCheck is false and the data is - * less than sizeof(). - * @details Use: readValue(×tamp, skipSizeCheck); - */ - template - T readValue(time_t *timestamp = nullptr, bool skipSizeCheck = false) { - NimBLEAttValue value = readValue(); - if(!skipSizeCheck && value.size() < sizeof(T)) return T(); - return *((T *)value.getValue(timestamp)); - } - -private: - - NimBLERemoteCharacteristic(NimBLERemoteService *pRemoteservice, const struct ble_gatt_chr *chr); - - friend class NimBLEClient; - friend class NimBLERemoteService; - friend class NimBLERemoteDescriptor; - - // Private member functions - bool setNotify(uint16_t val, notify_callback notifyCallback = nullptr, bool response = true); - bool retrieveDescriptors(const NimBLEUUID *uuid_filter = nullptr); - static int onReadCB(uint16_t conn_handle, const struct ble_gatt_error *error, - struct ble_gatt_attr *attr, void *arg); - static int onWriteCB(uint16_t conn_handle, const struct ble_gatt_error *error, - struct ble_gatt_attr *attr, void *arg); - static int descriptorDiscCB(uint16_t conn_handle, const struct ble_gatt_error *error, - uint16_t chr_val_handle, const struct ble_gatt_dsc *dsc, - void *arg); - static int nextCharCB(uint16_t conn_handle, const struct ble_gatt_error *error, - const struct ble_gatt_chr *chr, void *arg); - - // Private properties - NimBLEUUID m_uuid; - uint8_t m_charProp; - uint16_t m_handle; - uint16_t m_defHandle; - uint16_t m_endHandle; - NimBLERemoteService* m_pRemoteService; - NimBLEAttValue m_value; - notify_callback m_notifyCallback; - - // We maintain a vector of descriptors owned by this characteristic. - std::vector m_descriptorVector; }; // NimBLERemoteCharacteristic #endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL */ -#endif /* COMPONENTS_NIMBLEREMOTECHARACTERISTIC_H_ */ +#endif /* NIMBLE_CPP_REMOTE_CHARACTERISTIC_H_ */ diff --git a/lib/libesp32_div/esp-nimble-cpp/src/NimBLERemoteDescriptor.cpp b/lib/libesp32_div/esp-nimble-cpp/src/NimBLERemoteDescriptor.cpp index b4992f4ec..cdb54dc06 100644 --- a/lib/libesp32_div/esp-nimble-cpp/src/NimBLERemoteDescriptor.cpp +++ b/lib/libesp32_div/esp-nimble-cpp/src/NimBLERemoteDescriptor.cpp @@ -1,197 +1,50 @@ /* - * NimBLERemoteDescriptor.cpp + * Copyright 2020-2025 Ryan Powell and + * esp-nimble-cpp, NimBLE-Arduino contributors. * - * Created: on Jan 27 2020 - * Author H2zero + * 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 * - * Originally: + * http://www.apache.org/licenses/LICENSE-2.0 * - * BLERemoteDescriptor.cpp - * - * Created on: Jul 8, 2017 - * Author: kolban + * 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 "nimconfig.h" -#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL) - #include "NimBLERemoteDescriptor.h" -#include "NimBLEUtils.h" -#include "NimBLELog.h" +#if CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL -#include - -static const char* LOG_TAG = "NimBLERemoteDescriptor"; +# include "NimBLERemoteCharacteristic.h" /** * @brief Remote descriptor constructor. * @param [in] pRemoteCharacteristic A pointer to the Characteristic that this belongs to. * @param [in] dsc A pointer to the struct that contains the descriptor information. */ -NimBLERemoteDescriptor::NimBLERemoteDescriptor(NimBLERemoteCharacteristic* pRemoteCharacteristic, - const struct ble_gatt_dsc *dsc) -{ - NIMBLE_LOGD(LOG_TAG, ">> NimBLERemoteDescriptor()"); - switch (dsc->uuid.u.type) { - case BLE_UUID_TYPE_16: - m_uuid = NimBLEUUID(dsc->uuid.u16.value); - break; - case BLE_UUID_TYPE_32: - m_uuid = NimBLEUUID(dsc->uuid.u32.value); - break; - case BLE_UUID_TYPE_128: - m_uuid = NimBLEUUID(const_cast(&dsc->uuid.u128)); - break; - default: - break; - } - - m_handle = dsc->handle; - m_pRemoteCharacteristic = pRemoteCharacteristic; - - NIMBLE_LOGD(LOG_TAG, "<< NimBLERemoteDescriptor(): %s", m_uuid.toString().c_str()); -} - - -/** - * @brief Retrieve the handle associated with this remote descriptor. - * @return The handle associated with this remote descriptor. - */ -uint16_t NimBLERemoteDescriptor::getHandle() { - return m_handle; -} // getHandle - +NimBLERemoteDescriptor::NimBLERemoteDescriptor(const NimBLERemoteCharacteristic* pRemoteCharacteristic, + const ble_gatt_dsc* dsc) + : NimBLERemoteValueAttribute{dsc->uuid, dsc->handle}, + m_pRemoteCharacteristic{pRemoteCharacteristic} {} // NimBLERemoteDescriptor /** * @brief Get the characteristic that owns this descriptor. * @return The characteristic that owns this descriptor. */ -NimBLERemoteCharacteristic* NimBLERemoteDescriptor::getRemoteCharacteristic() { - return m_pRemoteCharacteristic; +NimBLERemoteCharacteristic* NimBLERemoteDescriptor::getRemoteCharacteristic() const { + return const_cast(m_pRemoteCharacteristic); } // getRemoteCharacteristic - -/** - * @brief Retrieve the UUID associated this remote descriptor. - * @return The UUID associated this remote descriptor. - */ -NimBLEUUID NimBLERemoteDescriptor::getUUID() { - return m_uuid; -} // getUUID - - -/** - * @brief Read the value of the remote descriptor. - * @return The value of the remote descriptor. - */ -NimBLEAttValue NimBLERemoteDescriptor::readValue() { - NIMBLE_LOGD(LOG_TAG, ">> Descriptor readValue: %s", toString().c_str()); - - NimBLEClient* pClient = getRemoteCharacteristic()->getRemoteService()->getClient(); - NimBLEAttValue value; - - if (!pClient->isConnected()) { - NIMBLE_LOGE(LOG_TAG, "Disconnected"); - return value; - } - - int rc = 0; - int retryCount = 1; - TaskHandle_t cur_task = xTaskGetCurrentTaskHandle(); - ble_task_data_t taskData = {this, cur_task, 0, &value}; - - do { - rc = ble_gattc_read_long(pClient->getConnId(), m_handle, 0, - NimBLERemoteDescriptor::onReadCB, - &taskData); - if (rc != 0) { - NIMBLE_LOGE(LOG_TAG, "Error: Failed to read descriptor; rc=%d, %s", - rc, NimBLEUtils::returnCodeToString(rc)); - return value; - } - -#ifdef ulTaskNotifyValueClear - // Clear the task notification value to ensure we block - ulTaskNotifyValueClear(cur_task, ULONG_MAX); -#endif - ulTaskNotifyTake(pdTRUE, portMAX_DELAY); - rc = taskData.rc; - - switch(rc){ - case 0: - case BLE_HS_EDONE: - rc = 0; - break; - // Descriptor is not long-readable, return with what we have. - case BLE_HS_ATT_ERR(BLE_ATT_ERR_ATTR_NOT_LONG): - NIMBLE_LOGI(LOG_TAG, "Attribute not long"); - rc = 0; - break; - case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_AUTHEN): - case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_AUTHOR): - case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_ENC): - if (retryCount && pClient->secureConnection()) - break; - /* Else falls through. */ - default: - return value; - } - } while(rc != 0 && retryCount--); - - NIMBLE_LOGD(LOG_TAG, "<< Descriptor readValue(): length: %u rc=%d", value.length(), rc); - return value; -} // readValue - - -/** - * @brief Callback for Descriptor read operation. - * @return success == 0 or error code. - */ -int NimBLERemoteDescriptor::onReadCB(uint16_t conn_handle, - const struct ble_gatt_error *error, - struct ble_gatt_attr *attr, void *arg) -{ - (void)attr; - ble_task_data_t *pTaskData = (ble_task_data_t*)arg; - NimBLERemoteDescriptor* desc = (NimBLERemoteDescriptor*)pTaskData->pATT; - uint16_t conn_id = desc->getRemoteCharacteristic()->getRemoteService()->getClient()->getConnId(); - - if(conn_id != conn_handle){ - return 0; - } - - NIMBLE_LOGD(LOG_TAG, "Read complete; status=%d conn_handle=%d", error->status, conn_handle); - - NimBLEAttValue *valBuf = (NimBLEAttValue*)pTaskData->buf; - int rc = error->status; - - if(rc == 0) { - if(attr) { - uint16_t data_len = OS_MBUF_PKTLEN(attr->om); - if((valBuf->size() + data_len) > BLE_ATT_ATTR_MAX_LEN) { - rc = BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; - } else { - NIMBLE_LOGD(LOG_TAG, "Got %u bytes", data_len); - valBuf->append(attr->om->om_data, data_len); - return 0; - } - } - } - - pTaskData->rc = rc; - xTaskNotifyGive(pTaskData->task); - - return rc; -} - - /** * @brief Return a string representation of this Remote Descriptor. * @return A string representation of this Remote Descriptor. */ -std::string NimBLERemoteDescriptor::toString() { +std::string NimBLERemoteDescriptor::toString() const { std::string res = "Descriptor: uuid: " + getUUID().toString(); - char val[6]; + char val[6]; res += ", handle: "; snprintf(val, sizeof(val), "%d", getHandle()); res += val; @@ -199,137 +52,8 @@ std::string NimBLERemoteDescriptor::toString() { return res; } // toString - -/** - * @brief Callback for descriptor write operation. - * @return success == 0 or error code. - */ -int NimBLERemoteDescriptor::onWriteCB(uint16_t conn_handle, - const struct ble_gatt_error *error, - struct ble_gatt_attr *attr, void *arg) -{ - ble_task_data_t *pTaskData = (ble_task_data_t*)arg; - NimBLERemoteDescriptor* descriptor = (NimBLERemoteDescriptor*)pTaskData->pATT; - - if(descriptor->getRemoteCharacteristic()->getRemoteService()->getClient()->getConnId() != conn_handle){ - return 0; - } - - NIMBLE_LOGI(LOG_TAG, "Write complete; status=%d conn_handle=%d", error->status, conn_handle); - - pTaskData->rc = error->status; - xTaskNotifyGive(pTaskData->task); - - return 0; +NimBLEClient* NimBLERemoteDescriptor::getClient() const { + return m_pRemoteCharacteristic->getClient(); } - -/** - * @brief Write a new value to a remote descriptor from a std::vector. - * @param [in] vec A std::vector value to write to the remote descriptor. - * @param [in] response Whether we require a response from the write. - * @return false if not connected or otherwise cannot perform write. - */ -bool NimBLERemoteDescriptor::writeValue(const std::vector& vec, bool response) { - return writeValue((uint8_t*)&vec[0], vec.size(), response); -} // writeValue - - -/** - * @brief Write a new value to the remote descriptor from a const char*. - * @param [in] char_s A character string to write to the remote descriptor. - * @param [in] response Whether we require a response from the write. - * @return false if not connected or otherwise cannot perform write. - */ -bool NimBLERemoteDescriptor::writeValue(const char* char_s, bool response) { - return writeValue((uint8_t*)char_s, strlen(char_s), response); -} // writeValue - - -/** - * @brief Write a new value to a remote descriptor. - * @param [in] data The data to send to the remote descriptor. - * @param [in] length The length of the data to send. - * @param [in] response True if we expect a write response. - * @return false if not connected or otherwise cannot perform write. - */ -bool NimBLERemoteDescriptor::writeValue(const uint8_t* data, size_t length, bool response) { - - NIMBLE_LOGD(LOG_TAG, ">> Descriptor writeValue: %s", toString().c_str()); - - NimBLEClient* pClient = getRemoteCharacteristic()->getRemoteService()->getClient(); - - // Check to see that we are connected. - if (!pClient->isConnected()) { - NIMBLE_LOGE(LOG_TAG, "Disconnected"); - return false; - } - - int rc = 0; - int retryCount = 1; - uint16_t mtu = ble_att_mtu(pClient->getConnId()) - 3; - - // Check if the data length is longer than we can write in 1 connection event. - // If so we must do a long write which requires a response. - if(length <= mtu && !response) { - rc = ble_gattc_write_no_rsp_flat(pClient->getConnId(), m_handle, data, length); - return (rc == 0); - } - - TaskHandle_t cur_task = xTaskGetCurrentTaskHandle(); - ble_task_data_t taskData = {this, cur_task, 0, nullptr}; - - do { - if(length > mtu) { - NIMBLE_LOGI(LOG_TAG,"long write %d bytes", length); - os_mbuf *om = ble_hs_mbuf_from_flat(data, length); - rc = ble_gattc_write_long(pClient->getConnId(), m_handle, 0, om, - NimBLERemoteDescriptor::onWriteCB, - &taskData); - } else { - rc = ble_gattc_write_flat(pClient->getConnId(), m_handle, - data, length, - NimBLERemoteDescriptor::onWriteCB, - &taskData); - } - - if (rc != 0) { - NIMBLE_LOGE(LOG_TAG, "Error: Failed to write descriptor; rc=%d", rc); - return false; - } - -#ifdef ulTaskNotifyValueClear - // Clear the task notification value to ensure we block - ulTaskNotifyValueClear(cur_task, ULONG_MAX); -#endif - ulTaskNotifyTake(pdTRUE, portMAX_DELAY); - rc = taskData.rc; - - switch(rc) { - case 0: - case BLE_HS_EDONE: - rc = 0; - break; - case BLE_HS_ATT_ERR(BLE_ATT_ERR_ATTR_NOT_LONG): - NIMBLE_LOGE(LOG_TAG, "Long write not supported by peer; Truncating length to %d", mtu); - retryCount++; - length = mtu; - break; - - case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_AUTHEN): - case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_AUTHOR): - case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_ENC): - if (retryCount && pClient->secureConnection()) - break; - /* Else falls through. */ - default: - return false; - } - } while(rc != 0 && retryCount--); - - NIMBLE_LOGD(LOG_TAG, "<< Descriptor writeValue, rc: %d",rc); - return (rc == 0); -} // writeValue - - -#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL */ +#endif // CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL diff --git a/lib/libesp32_div/esp-nimble-cpp/src/NimBLERemoteDescriptor.h b/lib/libesp32_div/esp-nimble-cpp/src/NimBLERemoteDescriptor.h index 756beb385..349988c28 100644 --- a/lib/libesp32_div/esp-nimble-cpp/src/NimBLERemoteDescriptor.h +++ b/lib/libesp32_div/esp-nimble-cpp/src/NimBLERemoteDescriptor.h @@ -1,104 +1,48 @@ /* - * NimBLERemoteDescriptor.h + * Copyright 2020-2025 Ryan Powell and + * esp-nimble-cpp, NimBLE-Arduino contributors. * - * Created: on Jan 27 2020 - * Author H2zero + * 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 * - * Originally: + * http://www.apache.org/licenses/LICENSE-2.0 * - * BLERemoteDescriptor.h - * - * Created on: Jul 8, 2017 - * Author: kolban + * 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 COMPONENTS_NIMBLEREMOTEDESCRIPTOR_H_ -#define COMPONENTS_NIMBLEREMOTEDESCRIPTOR_H_ +#ifndef NIMBLE_CPP_REMOTE_DESCRIPTOR_H_ +#define NIMBLE_CPP_REMOTE_DESCRIPTOR_H_ #include "nimconfig.h" -#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL) +#if CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL -#include "NimBLERemoteCharacteristic.h" +# include "NimBLERemoteValueAttribute.h" class NimBLERemoteCharacteristic; +class NimBLEClient; + /** - * @brief A model of remote %BLE descriptor. + * @brief A model of remote BLE descriptor. */ -class NimBLERemoteDescriptor { -public: - uint16_t getHandle(); - NimBLERemoteCharacteristic* getRemoteCharacteristic(); - NimBLEUUID getUUID(); - NimBLEAttValue readValue(); - std::string toString(void); - bool writeValue(const uint8_t* data, size_t length, bool response = false); - bool writeValue(const std::vector& v, bool response = false); - bool writeValue(const char* s, bool response = false); +class NimBLERemoteDescriptor : public NimBLERemoteValueAttribute { + public: + NimBLERemoteCharacteristic* getRemoteCharacteristic() const; + std::string toString(void) const; + NimBLEClient* getClient() const override; + private: + friend class NimBLERemoteCharacteristic; - /*********************** Template Functions ************************/ + NimBLERemoteDescriptor(const NimBLERemoteCharacteristic* pRemoteCharacteristic, const ble_gatt_dsc* dsc); + ~NimBLERemoteDescriptor() = default; - /** - * @brief Template to set the remote descriptor value to val. - * @param [in] s The value to write. - * @param [in] response True == request write response. - * @details Only used for non-arrays and types without a `c_str()` method. - */ - template -#ifdef _DOXYGEN_ - bool -#else - typename std::enable_if::value && !Has_c_str_len::value, bool>::type -#endif - writeValue(const T& s, bool response = false) { - return writeValue((uint8_t*)&s, sizeof(T), response); - } - - /** - * @brief Template to set the remote descriptor value to val. - * @param [in] s The value to write. - * @param [in] response True == request write response. - * @details Only used if the has a `c_str()` method. - */ - template -#ifdef _DOXYGEN_ - bool -#else - typename std::enable_if::value, bool>::type -#endif - writeValue(const T& s, bool response = false) { - return writeValue((uint8_t*)s.c_str(), s.length(), response); - } - - /** - * @brief Template to convert the remote descriptor data to . - * @tparam T The type to convert the data to. - * @param [in] skipSizeCheck If true it will skip checking if the data size is less than sizeof(). - * @return The data converted to or NULL if skipSizeCheck is false and the data is - * less than sizeof(). - * @details Use: readValue(skipSizeCheck); - */ - template - T readValue(bool skipSizeCheck = false) { - NimBLEAttValue value = readValue(); - if(!skipSizeCheck && value.size() < sizeof(T)) return T(); - return *((T *)value.data()); - } - -private: - friend class NimBLERemoteCharacteristic; - - NimBLERemoteDescriptor (NimBLERemoteCharacteristic* pRemoteCharacteristic, - const struct ble_gatt_dsc *dsc); - static int onWriteCB(uint16_t conn_handle, const struct ble_gatt_error *error, - struct ble_gatt_attr *attr, void *arg); - static int onReadCB(uint16_t conn_handle, const struct ble_gatt_error *error, - struct ble_gatt_attr *attr, void *arg); - - uint16_t m_handle; - NimBLEUUID m_uuid; - NimBLERemoteCharacteristic* m_pRemoteCharacteristic; + const NimBLERemoteCharacteristic* m_pRemoteCharacteristic; }; -#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL */ -#endif /* COMPONENTS_NIMBLEREMOTEDESCRIPTOR_H_ */ +#endif // CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL +#endif // NIMBLE_CPP_REMOTE_DESCRIPTOR_H_ diff --git a/lib/libesp32_div/esp-nimble-cpp/src/NimBLERemoteService.cpp b/lib/libesp32_div/esp-nimble-cpp/src/NimBLERemoteService.cpp index 5a72fe368..fd9aeec2b 100644 --- a/lib/libesp32_div/esp-nimble-cpp/src/NimBLERemoteService.cpp +++ b/lib/libesp32_div/esp-nimble-cpp/src/NimBLERemoteService.cpp @@ -1,26 +1,30 @@ /* - * NimBLERemoteService.cpp + * Copyright 2020-2025 Ryan Powell and + * esp-nimble-cpp, NimBLE-Arduino contributors. * - * Created: on Jan 27 2020 - * Author H2zero + * 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 * - * Originally: + * http://www.apache.org/licenses/LICENSE-2.0 * - * BLERemoteService.cpp - * - * Created on: Jul 8, 2017 - * Author: kolban + * 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 "nimconfig.h" -#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL) - #include "NimBLERemoteService.h" -#include "NimBLEUtils.h" -#include "NimBLEDevice.h" -#include "NimBLELog.h" +#if CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL -#include +# include "NimBLERemoteCharacteristic.h" +# include "NimBLEClient.h" +# include "NimBLEAttValue.h" +# include "NimBLEUtils.h" +# include "NimBLELog.h" + +# include static const char* LOG_TAG = "NimBLERemoteService"; @@ -29,28 +33,8 @@ static const char* LOG_TAG = "NimBLERemoteService"; * @param [in] pClient A pointer to the client this belongs to. * @param [in] service A pointer to the structure with the service information. */ -NimBLERemoteService::NimBLERemoteService(NimBLEClient* pClient, const struct ble_gatt_svc* service) { - - NIMBLE_LOGD(LOG_TAG, ">> NimBLERemoteService()"); - m_pClient = pClient; - switch (service->uuid.u.type) { - case BLE_UUID_TYPE_16: - m_uuid = NimBLEUUID(service->uuid.u16.value); - break; - case BLE_UUID_TYPE_32: - m_uuid = NimBLEUUID(service->uuid.u32.value); - break; - case BLE_UUID_TYPE_128: - m_uuid = NimBLEUUID(const_cast(&service->uuid.u128)); - break; - default: - break; - } - m_startHandle = service->start_handle; - m_endHandle = service->end_handle; - NIMBLE_LOGD(LOG_TAG, "<< NimBLERemoteService(): %s", m_uuid.toString().c_str()); -} - +NimBLERemoteService::NimBLERemoteService(NimBLEClient* pClient, const ble_gatt_svc* service) + : NimBLEAttribute{service->uuid, service->start_handle}, m_pClient{pClient}, m_endHandle{service->end_handle} {} /** * @brief When deleting the service make sure we delete all characteristics and descriptors. @@ -59,66 +43,62 @@ NimBLERemoteService::~NimBLERemoteService() { deleteCharacteristics(); } - /** * @brief Get iterator to the beginning of the vector of remote characteristic pointers. * @return An iterator to the beginning of the vector of remote characteristic pointers. */ -std::vector::iterator NimBLERemoteService::begin() { - return m_characteristicVector.begin(); +std::vector::iterator NimBLERemoteService::begin() const { + return m_vChars.begin(); } - /** * @brief Get iterator to the end of the vector of remote characteristic pointers. * @return An iterator to the end of the vector of remote characteristic pointers. */ -std::vector::iterator NimBLERemoteService::end() { - return m_characteristicVector.end(); +std::vector::iterator NimBLERemoteService::end() const { + return m_vChars.end(); } - /** * @brief Get the remote characteristic object for the characteristic UUID. * @param [in] uuid Remote characteristic uuid. * @return A pointer to the remote characteristic object. */ -NimBLERemoteCharacteristic* NimBLERemoteService::getCharacteristic(const char* uuid) { +NimBLERemoteCharacteristic* NimBLERemoteService::getCharacteristic(const char* uuid) const { return getCharacteristic(NimBLEUUID(uuid)); } // getCharacteristic - /** * @brief Get the characteristic object for the UUID. * @param [in] uuid Characteristic uuid. * @return A pointer to the characteristic object, or nullptr if not found. */ -NimBLERemoteCharacteristic* NimBLERemoteService::getCharacteristic(const NimBLEUUID &uuid) { +NimBLERemoteCharacteristic* NimBLERemoteService::getCharacteristic(const NimBLEUUID& uuid) const { NIMBLE_LOGD(LOG_TAG, ">> getCharacteristic: uuid: %s", uuid.toString().c_str()); + NimBLERemoteCharacteristic* pChar = nullptr; + size_t prev_size = m_vChars.size(); - for(auto &it: m_characteristicVector) { - if(it->getUUID() == uuid) { - NIMBLE_LOGD(LOG_TAG, "<< getCharacteristic: found the characteristic with uuid: %s", uuid.toString().c_str()); - return it; + for (const auto& it : m_vChars) { + if (it->getUUID() == uuid) { + pChar = it; + goto Done; } } - size_t prev_size = m_characteristicVector.size(); - if(retrieveCharacteristics(&uuid)) { - if(m_characteristicVector.size() > prev_size) { - return m_characteristicVector.back(); + if (retrieveCharacteristics(&uuid)) { + if (m_vChars.size() > prev_size) { + pChar = m_vChars.back(); + goto Done; } // If the request was successful but 16/32 bit uuid not found // try again with the 128 bit uuid. - if(uuid.bitSize() == BLE_UUID_TYPE_16 || - uuid.bitSize() == BLE_UUID_TYPE_32) - { + if (uuid.bitSize() == BLE_UUID_TYPE_16 || uuid.bitSize() == BLE_UUID_TYPE_32) { NimBLEUUID uuid128(uuid); uuid128.to128(); if (retrieveCharacteristics(&uuid128)) { - if(m_characteristicVector.size() > prev_size) { - return m_characteristicVector.back(); + if (m_vChars.size() > prev_size) { + pChar = m_vChars.back(); } } } else { @@ -128,286 +108,198 @@ NimBLERemoteCharacteristic* NimBLERemoteService::getCharacteristic(const NimBLEU uuid16.to16(); // if the uuid was 128 bit but not of the BLE base type this check will fail if (uuid16.bitSize() == BLE_UUID_TYPE_16) { - if(retrieveCharacteristics(&uuid16)) { - if(m_characteristicVector.size() > prev_size) { - return m_characteristicVector.back(); + if (retrieveCharacteristics(&uuid16)) { + if (m_vChars.size() > prev_size) { + pChar = m_vChars.back(); } } } } } - NIMBLE_LOGD(LOG_TAG, "<< getCharacteristic: not found"); - return nullptr; +Done: + NIMBLE_LOGD(LOG_TAG, "<< Characteristic %sfound", pChar ? "" : "not "); + return pChar; } // getCharacteristic - /** * @brief Get a pointer to the vector of found characteristics. * @param [in] refresh If true the current characteristics vector will cleared and * all characteristics for this service retrieved from the peripheral. * If false the vector will be returned with the currently stored characteristics of this service. - * @return A pointer to the vector of descriptors for this characteristic. + * @return A read-only reference to the vector of characteristics retrieved for this service. */ -std::vector* NimBLERemoteService::getCharacteristics(bool refresh) { - if(refresh) { +const std::vector& NimBLERemoteService::getCharacteristics(bool refresh) const { + if (refresh) { deleteCharacteristics(); - - if (!retrieveCharacteristics()) { - NIMBLE_LOGE(LOG_TAG, "Error: Failed to get characteristics"); - } - else{ - NIMBLE_LOGI(LOG_TAG, "Found %d characteristics", m_characteristicVector.size()); - } + retrieveCharacteristics(); } - return &m_characteristicVector; -} // getCharacteristics + return m_vChars; +} // getCharacteristics /** * @brief Callback for Characteristic discovery. * @return success == 0 or error code. */ -int NimBLERemoteService::characteristicDiscCB(uint16_t conn_handle, - const struct ble_gatt_error *error, - const struct ble_gatt_chr *chr, void *arg) -{ - NIMBLE_LOGD(LOG_TAG,"Characteristic Discovered >> status: %d handle: %d", - error->status, (error->status == 0) ? chr->val_handle : -1); +int NimBLERemoteService::characteristicDiscCB(uint16_t conn_handle, + const ble_gatt_error* error, + const ble_gatt_chr* chr, + void* arg) { + NIMBLE_LOGD(LOG_TAG, + "Characteristic Discovery >> status: %d handle: %d", + error->status, + (error->status == 0) ? chr->def_handle : -1); + auto pTaskData = (NimBLETaskData*)arg; + const auto pSvc = (NimBLERemoteService*)pTaskData->m_pInstance; - ble_task_data_t *pTaskData = (ble_task_data_t*)arg; - NimBLERemoteService *service = (NimBLERemoteService*)pTaskData->pATT; + if (error->status == BLE_HS_ENOTCONN) { + NIMBLE_LOGE(LOG_TAG, "<< Characteristic Discovery; Not connected"); + NimBLEUtils::taskRelease(*pTaskData, error->status); + return error->status; + } // Make sure the discovery is for this device - if(service->getClient()->getConnId() != conn_handle){ + if (pSvc->getClient()->getConnHandle() != conn_handle) { return 0; } - if(error->status == 0) { - // Found a service - add it to the vector - NimBLERemoteCharacteristic* pRemoteCharacteristic = new NimBLERemoteCharacteristic(service, chr); - service->m_characteristicVector.push_back(pRemoteCharacteristic); + if (error->status == 0) { + pSvc->m_vChars.push_back(new NimBLERemoteCharacteristic(pSvc, chr)); return 0; } - if(error->status == BLE_HS_EDONE) { - pTaskData->rc = 0; - } else { - NIMBLE_LOGE(LOG_TAG, "characteristicDiscCB() rc=%d %s", - error->status, - NimBLEUtils::returnCodeToString(error->status)); - pTaskData->rc = error->status; - } - - xTaskNotifyGive(pTaskData->task); - - NIMBLE_LOGD(LOG_TAG,"<< Characteristic Discovered"); + NimBLEUtils::taskRelease(*pTaskData, error->status); + NIMBLE_LOGD(LOG_TAG, "<< Characteristic Discovery"); return error->status; } - /** * @brief Retrieve all the characteristics for this service. * This function will not return until we have all the characteristics. * @return True if successful. */ -bool NimBLERemoteService::retrieveCharacteristics(const NimBLEUUID *uuid_filter) { - NIMBLE_LOGD(LOG_TAG, ">> retrieveCharacteristics() for service: %s", getUUID().toString().c_str()); +bool NimBLERemoteService::retrieveCharacteristics(const NimBLEUUID* uuidFilter) const { + NIMBLE_LOGD(LOG_TAG, ">> retrieveCharacteristics()"); + int rc = 0; + NimBLETaskData taskData(const_cast(this)); - int rc = 0; - TaskHandle_t cur_task = xTaskGetCurrentTaskHandle(); - ble_task_data_t taskData = {this, cur_task, 0, nullptr}; - - if(uuid_filter == nullptr) { - rc = ble_gattc_disc_all_chrs(m_pClient->getConnId(), - m_startHandle, - m_endHandle, - NimBLERemoteService::characteristicDiscCB, - &taskData); + if (uuidFilter == nullptr) { + rc = ble_gattc_disc_all_chrs(m_pClient->getConnHandle(), + getHandle(), + getEndHandle(), + NimBLERemoteService::characteristicDiscCB, + &taskData); } else { - rc = ble_gattc_disc_chrs_by_uuid(m_pClient->getConnId(), - m_startHandle, - m_endHandle, - &uuid_filter->getNative()->u, - NimBLERemoteService::characteristicDiscCB, - &taskData); + rc = ble_gattc_disc_chrs_by_uuid(m_pClient->getConnHandle(), + getHandle(), + getEndHandle(), + uuidFilter->getBase(), + NimBLERemoteService::characteristicDiscCB, + &taskData); } if (rc != 0) { - NIMBLE_LOGE(LOG_TAG, "ble_gattc_disc_all_chrs: rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc)); + NIMBLE_LOGE(LOG_TAG, "ble_gattc_disc_chrs rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc)); return false; } -#ifdef ulTaskNotifyValueClear - // Clear the task notification value to ensure we block - ulTaskNotifyValueClear(cur_task, ULONG_MAX); -#endif - ulTaskNotifyTake(pdTRUE, portMAX_DELAY); - - if(taskData.rc == 0){ - if (uuid_filter == nullptr) { - if (m_characteristicVector.size() > 1) { - for (auto it = m_characteristicVector.begin(); it != m_characteristicVector.end(); ++it ) { - auto nx = std::next(it, 1); - if (nx == m_characteristicVector.end()) { - break; - } - (*it)->m_endHandle = (*nx)->m_defHandle - 1; - } - } - - if (m_characteristicVector.size() > 0) { - m_characteristicVector.back()->m_endHandle = getEndHandle(); - } - } - + NimBLEUtils::taskWait(taskData, BLE_NPL_TIME_FOREVER); + rc = taskData.m_flags; + if (rc == 0 || rc == BLE_HS_EDONE) { NIMBLE_LOGD(LOG_TAG, "<< retrieveCharacteristics()"); return true; } - NIMBLE_LOGE(LOG_TAG, "Could not retrieve characteristics"); + NIMBLE_LOGE(LOG_TAG, "<< retrieveCharacteristics() rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc)); return false; - } // retrieveCharacteristics - /** * @brief Get the client associated with this service. * @return A reference to the client associated with this service. */ -NimBLEClient* NimBLERemoteService::getClient() { +NimBLEClient* NimBLERemoteService::getClient() const { return m_pClient; } // getClient - -/** - * @brief Get the service end handle. - */ -uint16_t NimBLERemoteService::getEndHandle() { - return m_endHandle; -} // getEndHandle - - -/** - * @brief Get the service start handle. - */ -uint16_t NimBLERemoteService::getStartHandle() { - return m_startHandle; -} // getStartHandle - - -/** - * @brief Get the service UUID. - */ -NimBLEUUID NimBLERemoteService::getUUID() { - return m_uuid; -} - - /** * @brief Read the value of a characteristic associated with this service. - * @param [in] characteristicUuid The characteristic to read. + * @param [in] uuid The characteristic to read. * @returns a string containing the value or an empty string if not found or error. */ -std::string NimBLERemoteService::getValue(const NimBLEUUID &characteristicUuid) { - NIMBLE_LOGD(LOG_TAG, ">> readValue: uuid: %s", characteristicUuid.toString().c_str()); - - std::string ret = ""; - NimBLERemoteCharacteristic* pChar = getCharacteristic(characteristicUuid); - - if(pChar != nullptr) { - ret = pChar->readValue(); +NimBLEAttValue NimBLERemoteService::getValue(const NimBLEUUID& uuid) const { + const auto pChar = getCharacteristic(uuid); + if (pChar) { + return pChar->readValue(); } - NIMBLE_LOGD(LOG_TAG, "<< readValue"); - return ret; + return NimBLEAttValue{}; } // readValue - /** * @brief Set the value of a characteristic. - * @param [in] characteristicUuid The characteristic to set. + * @param [in] uuid The characteristic UUID to set. * @param [in] value The value to set. * @returns true on success, false if not found or error */ -bool NimBLERemoteService::setValue(const NimBLEUUID &characteristicUuid, const std::string &value) { - NIMBLE_LOGD(LOG_TAG, ">> setValue: uuid: %s", characteristicUuid.toString().c_str()); - - bool ret = false; - NimBLERemoteCharacteristic* pChar = getCharacteristic(characteristicUuid); - - if(pChar != nullptr) { - ret = pChar->writeValue(value); +bool NimBLERemoteService::setValue(const NimBLEUUID& uuid, const NimBLEAttValue& value) const { + const auto pChar = getCharacteristic(uuid); + if (pChar) { + return pChar->writeValue(value); } - NIMBLE_LOGD(LOG_TAG, "<< setValue"); - return ret; + return false; } // setValue - /** * @brief Delete the characteristics in the characteristics vector. * @details We maintain a vector called m_characteristicsVector that contains pointers to BLERemoteCharacteristic * object references. Since we allocated these in this class, we are also responsible for deleting * them. This method does just that. */ -void NimBLERemoteService::deleteCharacteristics() { - NIMBLE_LOGD(LOG_TAG, ">> deleteCharacteristics"); - for(auto &it: m_characteristicVector) { +void NimBLERemoteService::deleteCharacteristics() const { + for (const auto& it : m_vChars) { delete it; } - m_characteristicVector.clear(); - NIMBLE_LOGD(LOG_TAG, "<< deleteCharacteristics"); + std::vector{}.swap(m_vChars); } // deleteCharacteristics - /** * @brief Delete characteristic by UUID * @param [in] uuid The UUID of the characteristic to be removed from the local database. * @return Number of characteristics left. */ -size_t NimBLERemoteService::deleteCharacteristic(const NimBLEUUID &uuid) { - NIMBLE_LOGD(LOG_TAG, ">> deleteCharacteristic"); - - for(auto it = m_characteristicVector.begin(); it != m_characteristicVector.end(); ++it) { - if((*it)->getUUID() == uuid) { - delete *it; - m_characteristicVector.erase(it); +size_t NimBLERemoteService::deleteCharacteristic(const NimBLEUUID& uuid) const { + for (auto it = m_vChars.begin(); it != m_vChars.end(); ++it) { + if ((*it)->getUUID() == uuid) { + delete (*it); + m_vChars.erase(it); break; } } - NIMBLE_LOGD(LOG_TAG, "<< deleteCharacteristic"); - - return m_characteristicVector.size(); + return m_vChars.size(); } // deleteCharacteristic - /** * @brief Create a string representation of this remote service. * @return A string representation of this remote service. */ -std::string NimBLERemoteService::toString() { - std::string res = "Service: uuid: " + m_uuid.toString(); - char val[6]; - res += ", start_handle: "; - snprintf(val, sizeof(val), "%d", m_startHandle); +std::string NimBLERemoteService::toString() const { + std::string res = "Service: uuid: " + m_uuid.toString() + ", start_handle: 0x"; + char val[5]; + snprintf(val, sizeof(val), "%04x", getHandle()); res += val; - snprintf(val, sizeof(val), "%04x", m_startHandle); - res += " 0x"; - res += val; - res += ", end_handle: "; - snprintf(val, sizeof(val), "%d", m_endHandle); - res += val; - snprintf(val, sizeof(val), "%04x", m_endHandle); - res += " 0x"; + res += ", end_handle: 0x"; + snprintf(val, sizeof(val), "%04x", getEndHandle()); res += val; - for (auto &it: m_characteristicVector) { - res += "\n" + it->toString(); + for (const auto& chr : m_vChars) { + res += "\n" + chr->toString(); } return res; } // toString -#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL */ +#endif // CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL diff --git a/lib/libesp32_div/esp-nimble-cpp/src/NimBLERemoteService.h b/lib/libesp32_div/esp-nimble-cpp/src/NimBLERemoteService.h index 0443cfd99..6aebbabae 100644 --- a/lib/libesp32_div/esp-nimble-cpp/src/NimBLERemoteService.h +++ b/lib/libesp32_div/esp-nimble-cpp/src/NimBLERemoteService.h @@ -1,85 +1,68 @@ /* - * NimBLERemoteService.h + * Copyright 2020-2025 Ryan Powell and + * esp-nimble-cpp, NimBLE-Arduino contributors. * - * Created: on Jan 27 2020 - * Author H2zero + * 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 * - * Originally: + * http://www.apache.org/licenses/LICENSE-2.0 * - * BLERemoteService.h - * - * Created on: Jul 8, 2017 - * Author: kolban + * 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 COMPONENTS_NIMBLEREMOTESERVICE_H_ -#define COMPONENTS_NIMBLEREMOTESERVICE_H_ +#ifndef NIMBLE_CPP_REMOTE_SERVICE_H_ +#define NIMBLE_CPP_REMOTE_SERVICE_H_ #include "nimconfig.h" -#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL) +#if CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL -#include "NimBLEClient.h" -#include "NimBLEUUID.h" -#include "NimBLERemoteCharacteristic.h" +# include "NimBLEAttribute.h" +# include -#include - -class NimBLEClient; class NimBLERemoteCharacteristic; - +class NimBLEClient; +class NimBLEAttValue; /** - * @brief A model of a remote %BLE service. + * @brief A model of a remote BLE service. */ -class NimBLERemoteService { -public: - virtual ~NimBLERemoteService(); +class NimBLERemoteService : public NimBLEAttribute { + public: + NimBLERemoteCharacteristic* getCharacteristic(const char* uuid) const; + NimBLERemoteCharacteristic* getCharacteristic(const NimBLEUUID& uuid) const; + void deleteCharacteristics() const; + size_t deleteCharacteristic(const NimBLEUUID& uuid) const; + NimBLEClient* getClient(void) const; + NimBLEAttValue getValue(const NimBLEUUID& characteristicUuid) const; + bool setValue(const NimBLEUUID& characteristicUuid, const NimBLEAttValue& value) const; + std::string toString(void) const; + uint16_t getStartHandle() const { return getHandle(); } + uint16_t getEndHandle() const { return m_endHandle; } - // Public methods - std::vector::iterator begin(); - std::vector::iterator end(); - NimBLERemoteCharacteristic* getCharacteristic(const char* uuid); - NimBLERemoteCharacteristic* getCharacteristic(const NimBLEUUID &uuid); - void deleteCharacteristics(); - size_t deleteCharacteristic(const NimBLEUUID &uuid); - NimBLEClient* getClient(void); - //uint16_t getHandle(); - NimBLEUUID getUUID(void); - std::string getValue(const NimBLEUUID &characteristicUuid); - bool setValue(const NimBLEUUID &characteristicUuid, - const std::string &value); - std::string toString(void); - std::vector* getCharacteristics(bool refresh = false); + const std::vector& getCharacteristics(bool refresh = false) const; + std::vector::iterator begin() const; + std::vector::iterator end() const; -private: - // Private constructor ... never meant to be created by a user application. - NimBLERemoteService(NimBLEClient* pClient, const struct ble_gatt_svc *service); - - // Friends + private: friend class NimBLEClient; - friend class NimBLERemoteCharacteristic; - // Private methods - bool retrieveCharacteristics(const NimBLEUUID *uuid_filter = nullptr); - static int characteristicDiscCB(uint16_t conn_handle, - const struct ble_gatt_error *error, - const struct ble_gatt_chr *chr, - void *arg); + NimBLERemoteService(NimBLEClient* pClient, const struct ble_gatt_svc* service); + ~NimBLERemoteService(); + bool retrieveCharacteristics(const NimBLEUUID* uuidFilter = nullptr) const; + static int characteristicDiscCB(uint16_t conn_handle, + const struct ble_gatt_error* error, + const struct ble_gatt_chr* chr, + void* arg); - uint16_t getStartHandle(); - uint16_t getEndHandle(); - void releaseSemaphores(); - - // Properties - - // We maintain a vector of characteristics owned by this service. - std::vector m_characteristicVector; - - NimBLEClient* m_pClient; - NimBLEUUID m_uuid; - uint16_t m_startHandle; - uint16_t m_endHandle; + mutable std::vector m_vChars{}; + NimBLEClient* m_pClient{nullptr}; + uint16_t m_endHandle{0}; }; // NimBLERemoteService -#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL */ -#endif /* COMPONENTS_NIMBLEREMOTESERVICE_H_ */ +#endif // CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL +#endif // NIMBLE_CPP_REMOTE_SERVICE_H_ diff --git a/lib/libesp32_div/esp-nimble-cpp/src/NimBLERemoteValueAttribute.cpp b/lib/libesp32_div/esp-nimble-cpp/src/NimBLERemoteValueAttribute.cpp new file mode 100644 index 000000000..e5e5611ff --- /dev/null +++ b/lib/libesp32_div/esp-nimble-cpp/src/NimBLERemoteValueAttribute.cpp @@ -0,0 +1,220 @@ +/* + * Copyright 2020-2025 Ryan Powell 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 "NimBLERemoteValueAttribute.h" +#if CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL + +# include "NimBLEClient.h" +# include "NimBLEUtils.h" +# include "NimBLELog.h" + +# include + +static const char* LOG_TAG = "NimBLERemoteValueAttribute"; + +bool NimBLERemoteValueAttribute::writeValue(const uint8_t* data, size_t length, bool response) const { + NIMBLE_LOGD(LOG_TAG, ">> writeValue()"); + + const NimBLEClient* pClient = getClient(); + int retryCount = 1; + int rc = 0; + uint16_t mtu = pClient->getMTU() - 3; + NimBLETaskData taskData(const_cast(this)); + + // Check if the data length is longer than we can write in one connection event. + // If so we must do a long write which requires a response. + if (length <= mtu && !response) { + rc = ble_gattc_write_no_rsp_flat(pClient->getConnHandle(), getHandle(), data, length); + goto Done; + } + + do { + if (length > mtu) { + NIMBLE_LOGI(LOG_TAG, "writeValue: long write"); + os_mbuf* om = ble_hs_mbuf_from_flat(data, length); + rc = ble_gattc_write_long(pClient->getConnHandle(), getHandle(), 0, om, NimBLERemoteValueAttribute::onWriteCB, &taskData); + } else { + rc = ble_gattc_write_flat(pClient->getConnHandle(), + getHandle(), + data, + length, + NimBLERemoteValueAttribute::onWriteCB, + &taskData); + } + + if (rc != 0) { + goto Done; + } + + NimBLEUtils::taskWait(taskData, BLE_NPL_TIME_FOREVER); + rc = taskData.m_flags; + switch (rc) { + case 0: + case BLE_HS_EDONE: + rc = 0; + break; + case BLE_HS_ATT_ERR(BLE_ATT_ERR_ATTR_NOT_LONG): + NIMBLE_LOGE(LOG_TAG, "Long write not supported by peer; Truncating length to %d", mtu); + retryCount++; + length = mtu; + break; + + case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_AUTHEN): + case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_AUTHOR): + case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_ENC): + if (retryCount && pClient->secureConnection()) break; + /* Else falls through. */ + default: + goto Done; + } + } while (rc != 0 && retryCount--); + +Done: + if (rc != 0) { + NIMBLE_LOGE(LOG_TAG, "<< writeValue failed, rc: %d %s", rc, NimBLEUtils::returnCodeToString(rc)); + } else { + NIMBLE_LOGD(LOG_TAG, "<< writeValue"); + } + + return (rc == 0); +} // writeValue + +/** + * @brief Callback for characteristic write operation. + * @return success == 0 or error code. + */ +int NimBLERemoteValueAttribute::onWriteCB(uint16_t conn_handle, const ble_gatt_error* error, ble_gatt_attr* attr, void* arg) { + auto pTaskData = static_cast(arg); + const auto pAtt = static_cast(pTaskData->m_pInstance); + + if (error->status == BLE_HS_ENOTCONN) { + NIMBLE_LOGE(LOG_TAG, "<< Write complete; Not connected"); + NimBLEUtils::taskRelease(*pTaskData, error->status); + return error->status; + } + + if (pAtt->getClient()->getConnHandle() != conn_handle) { + return 0; + } + + NIMBLE_LOGI(LOG_TAG, "Write complete; status=%d", error->status); + NimBLEUtils::taskRelease(*pTaskData, error->status); + return 0; +} + +/** + * @brief Read the value of the remote characteristic. + * @param [in] timestamp A pointer to a time_t struct to store the time the value was read. + * @return The value of the remote characteristic. + */ +NimBLEAttValue NimBLERemoteValueAttribute::readValue(time_t* timestamp) { + NIMBLE_LOGD(LOG_TAG, ">> readValue()"); + + NimBLEAttValue value{}; + const NimBLEClient* pClient = getClient(); + int rc = 0; + int retryCount = 1; + NimBLETaskData taskData(const_cast(this), 0, &value); + + do { + rc = ble_gattc_read_long(pClient->getConnHandle(), getHandle(), 0, NimBLERemoteValueAttribute::onReadCB, &taskData); + if (rc != 0) { + goto Done; + } + + NimBLEUtils::taskWait(taskData, BLE_NPL_TIME_FOREVER); + rc = taskData.m_flags; + switch (rc) { + case 0: + case BLE_HS_EDONE: + rc = 0; + break; + // Characteristic is not long-readable, return with what we have. + case BLE_HS_ATT_ERR(BLE_ATT_ERR_ATTR_NOT_LONG): + NIMBLE_LOGI(LOG_TAG, "Attribute not long"); + rc = ble_gattc_read(pClient->getConnHandle(), getHandle(), NimBLERemoteValueAttribute::onReadCB, &taskData); + if (rc != 0) { + goto Done; + } + retryCount++; + break; + case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_AUTHEN): + case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_AUTHOR): + case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_ENC): + if (retryCount && pClient->secureConnection()) break; + /* Else falls through. */ + default: + goto Done; + } + } while (rc != 0 && retryCount--); + + value.setTimeStamp(); + m_value = value; + if (timestamp != nullptr) { + *timestamp = value.getTimeStamp(); + } + +Done: + if (rc != 0) { + NIMBLE_LOGE(LOG_TAG, "<< readValue failed rc=%d, %s", rc, NimBLEUtils::returnCodeToString(rc)); + } else { + NIMBLE_LOGD(LOG_TAG, "<< readValue"); + } + + return value; +} // readValue + +/** + * @brief Callback for characteristic read operation. + * @return success == 0 or error code. + */ +int NimBLERemoteValueAttribute::onReadCB(uint16_t conn_handle, const ble_gatt_error* error, ble_gatt_attr* attr, void* arg) { + auto pTaskData = static_cast(arg); + const auto pAtt = static_cast(pTaskData->m_pInstance); + + if (error->status == BLE_HS_ENOTCONN) { + NIMBLE_LOGE(LOG_TAG, "<< Read complete; Not connected"); + NimBLEUtils::taskRelease(*pTaskData, error->status); + return error->status; + } + + if (pAtt->getClient()->getConnHandle() != conn_handle) { + return 0; + } + + int rc = error->status; + NIMBLE_LOGI(LOG_TAG, "Read complete; status=%d", rc); + + if (rc == 0) { + if (attr) { + auto valBuf = static_cast(pTaskData->m_pBuf); + uint16_t data_len = OS_MBUF_PKTLEN(attr->om); + if ((valBuf->size() + data_len) > BLE_ATT_ATTR_MAX_LEN) { + rc = BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } else { + NIMBLE_LOGD(LOG_TAG, "Got %u bytes", data_len); + valBuf->append(attr->om->om_data, data_len); + return 0; + } + } + } + + NimBLEUtils::taskRelease(*pTaskData, rc); + return rc; +} // onReadCB + +#endif // CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL diff --git a/lib/libesp32_div/esp-nimble-cpp/src/NimBLERemoteValueAttribute.h b/lib/libesp32_div/esp-nimble-cpp/src/NimBLERemoteValueAttribute.h new file mode 100644 index 000000000..89df172ee --- /dev/null +++ b/lib/libesp32_div/esp-nimble-cpp/src/NimBLERemoteValueAttribute.h @@ -0,0 +1,174 @@ +/* + * Copyright 2020-2025 Ryan Powell 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_REMOTE_VALUE_ATTRIBUTE_H_ +#define NIMBLE_CPP_REMOTE_VALUE_ATTRIBUTE_H_ + +#include "nimconfig.h" +#if CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL + +# if defined(CONFIG_NIMBLE_CPP_IDF) +# include +# else +# include +# endif + +/**** FIX COMPILATION ****/ +# undef min +# undef max +/**************************/ + +# include "NimBLEValueAttribute.h" +# include "NimBLEAttValue.h" + +class NimBLEClient; + +class NimBLERemoteValueAttribute : public NimBLEValueAttribute, public NimBLEAttribute { + public: + /** + * @brief Read the value of the remote attribute. + * @param [in] timestamp A pointer to a time_t struct to store the time the value was read. + * @return The value of the remote attribute. + */ + NimBLEAttValue readValue(time_t* timestamp = nullptr); + + /** + * Get the client instance that owns this attribute. + */ + virtual NimBLEClient* getClient() const = 0; + + /** + * @brief Write a new value to the remote characteristic from a data buffer. + * @param [in] data A pointer to a data buffer. + * @param [in] length The length of the data in the data buffer. + * @param [in] response Whether we require a response from the write. + * @return false if not connected or otherwise cannot perform write. + */ + bool writeValue(const uint8_t* data, size_t length, bool response = false) const; + + /** + * @brief Write a new value to the remote characteristic from a const char*. + * @param [in] str A character string to write to the remote characteristic. + * @param [in] length (optional) The length of the character string, uses strlen if omitted. + * @param [in] response Whether we require a response from the write. + * @return false if not connected or otherwise cannot perform write. + */ + bool writeValue(const char* str, size_t length = 0, bool response = false) const { + return writeValue(reinterpret_cast(str), length ? length : strlen(str), response); + } + +# if __cplusplus < 201703L + /** + * @brief Template to set the remote characteristic value to val. + * @param [in] v The value to write. + * @param [in] response True == request write response. + * @details Only used for types without a `c_str()` and `length()` or `data()` and `size()` method. + * size must be evaluatable by `sizeof()` if no length is provided. + */ + template +# ifdef _DOXYGEN_ + bool +# else + typename std::enable_if::value && !Has_c_str_length::value && !Has_data_size::value, bool>::type +# endif + writeValue(const T& v, bool response = false) const { + return writeValue(reinterpret_cast(&v), sizeof(T), response); + } + + /** + * @brief Template to set the remote characteristic value to val. + * @param [in] s The value to write. + * @param [in] response True == request write response. + * @details Only used if the has a `c_str()` and `length()` method. + */ + template +# ifdef _DOXYGEN_ + bool +# else + typename std::enable_if::value && !Has_data_size::value, bool>::type +# endif + writeValue(const T& s, bool response = false) const { + return writeValue(reinterpret_cast(s.c_str()), s.length(), response); + } + + /** + * @brief Template to set the remote characteristic value to val. + * @param [in] v The value to write. + * @param [in] response True == request write response. + * @details Only used if the has a `data()` and `size()` method. + */ + template +# ifdef _DOXYGEN_ + bool +# else + typename std::enable_if::value, bool>::type +# endif + writeValue(const T& v, bool response = false) const { + return writeValue(reinterpret_cast(v.data()), v.size(), response); + } + +# else + /** + * @brief Template to set the remote characteristic value to val. + * @param [in] s The value to write. + * @param [in] response True == request write response. + * @note This function is only available if the type T is not a pointer. + */ + template + typename std::enable_if::value, bool>::type writeValue(const T& v, bool response = false) const { + if constexpr (Has_data_size::value) { + return writeValue(reinterpret_cast(v.data()), v.size(), response); + } else if constexpr (Has_c_str_length::value) { + return writeValue(reinterpret_cast(v.c_str()), v.length(), response); + } else { + return writeValue(reinterpret_cast(&v), sizeof(v), response); + } + } +# endif + + /** + * @brief Template to convert the remote characteristic data to . + * @tparam T The type to convert the data to. + * @param [in] timestamp A pointer to a time_t struct to store the time the value was read. + * @param [in] skipSizeCheck If true it will skip checking if the data size is less than sizeof(). + * @return The data converted to or NULL if skipSizeCheck is false and the data is + * less than sizeof(). + * @details Use: readValue(×tamp, skipSizeCheck); + */ + template + T readValue(time_t* timestamp = nullptr, bool skipSizeCheck = false) { + readValue(); + return getValue(timestamp, skipSizeCheck); + } + + protected: + /** + * @brief Construct a new NimBLERemoteValueAttribute object. + */ + NimBLERemoteValueAttribute(const ble_uuid_any_t& uuid, uint16_t handle) : NimBLEAttribute{uuid, handle} {} + + /** + * @brief Destroy the NimBLERemoteValueAttribute object. + */ + virtual ~NimBLERemoteValueAttribute() = default; + + static int onReadCB(uint16_t conn_handle, const ble_gatt_error* error, ble_gatt_attr* attr, void* arg); + static int onWriteCB(uint16_t conn_handle, const ble_gatt_error* error, ble_gatt_attr* attr, void* arg); +}; + +#endif // CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL +#endif // NIMBLE_CPP_REMOTE_VALUE_ATTRIBUTE_H_ diff --git a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEScan.cpp b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEScan.cpp index b4fe6a921..713c84a19 100644 --- a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEScan.cpp +++ b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEScan.cpp @@ -1,53 +1,49 @@ /* - * NimBLEScan.cpp + * Copyright 2020-2025 Ryan Powell and + * esp-nimble-cpp, NimBLE-Arduino contributors. * - * Created: on Jan 24 2020 - * Author H2zero + * 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 * - * Originally: + * http://www.apache.org/licenses/LICENSE-2.0 * - * BLEScan.cpp - * - * Created on: Jul 1, 2017 - * Author: kolban + * 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 "nimconfig.h" -#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER) - #include "NimBLEScan.h" -#include "NimBLEDevice.h" -#include "NimBLELog.h" +#if CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_OBSERVER -#include -#include +# include "NimBLEDevice.h" +# include "NimBLELog.h" -static const char* LOG_TAG = "NimBLEScan"; +# include +# include +static const char* LOG_TAG = "NimBLEScan"; +static NimBLEScanCallbacks defaultScanCallbacks; /** - * @brief Scan constuctor. + * @brief Scan constructor. */ -NimBLEScan::NimBLEScan() { - m_scan_params.filter_policy = BLE_HCI_SCAN_FILT_NO_WL; - m_scan_params.passive = 1; // If set, don’t send scan requests to advertisers (i.e., don’t request additional advertising data). - m_scan_params.itvl = 0; // This is defined as the time interval from when the Controller started its last LE scan until it begins the subsequent LE scan. (units=0.625 msec) - m_scan_params.window = 0; // The duration of the LE scan. LE_Scan_Window shall be less than or equal to LE_Scan_Interval (units=0.625 msec) - m_scan_params.limited = 0; // If set, only discover devices in limited discoverable mode. - m_scan_params.filter_duplicates = 1; // If set, the controller ignores all but the first advertisement from each device. - m_pScanCallbacks = nullptr; - m_ignoreResults = false; - m_pTaskData = nullptr; - m_duration = BLE_HS_FOREVER; // make sure this is non-zero in the event of a host reset - m_maxResults = 0xFF; -} - +NimBLEScan::NimBLEScan() + : m_pScanCallbacks{&defaultScanCallbacks}, + // default interval + window, no whitelist scan filter,not limited scan, no scan response, filter_duplicates + m_scanParams{0, 0, BLE_HCI_SCAN_FILT_NO_WL, 0, 1, 1}, + m_pTaskData{nullptr}, + m_maxResults{0xFF} {} /** * @brief Scan destructor, release any allocated resources. */ NimBLEScan::~NimBLEScan() { - clearResults(); + for (const auto& dev : m_scanResults.m_deviceVec) { + delete dev; + } } /** @@ -55,132 +51,114 @@ NimBLEScan::~NimBLEScan() { * @param [in] event The event type for this event. * @param [in] param Parameter data for this event. */ -/*STATIC*/ int NimBLEScan::handleGapEvent(ble_gap_event* event, void* arg) { (void)arg; NimBLEScan* pScan = NimBLEDevice::getScan(); - switch(event->type) { - + switch (event->type) { case BLE_GAP_EVENT_EXT_DISC: case BLE_GAP_EVENT_DISC: { - if(pScan->m_ignoreResults) { - NIMBLE_LOGI(LOG_TAG, "Scan op in progress - ignoring results"); + if (!pScan->isScanning()) { + NIMBLE_LOGI(LOG_TAG, "Scan stopped, ignoring event"); return 0; } -#if CONFIG_BT_NIMBLE_EXT_ADV - const auto& disc = event->ext_disc; - const bool isLegacyAdv = disc.props & BLE_HCI_ADV_LEGACY_MASK; - const auto event_type = isLegacyAdv ? disc.legacy_event_type : disc.props; -#else - const auto& disc = event->disc; - const bool isLegacyAdv = true; - const auto event_type = disc.event_type; -#endif + +# if CONFIG_BT_NIMBLE_EXT_ADV + const auto& disc = event->ext_disc; + const bool isLegacyAdv = disc.props & BLE_HCI_ADV_LEGACY_MASK; + const auto event_type = isLegacyAdv ? disc.legacy_event_type : disc.props; +# else + const auto& disc = event->disc; + const bool isLegacyAdv = true; + const auto event_type = disc.event_type; +# endif NimBLEAddress advertisedAddress(disc.addr); - // Examine our list of ignored addresses and stop processing if we don't want to see it or are already connected - if(NimBLEDevice::isIgnored(advertisedAddress)) { - NIMBLE_LOGI(LOG_TAG, "Ignoring device: address: %s", advertisedAddress.toString().c_str()); +# if CONFIG_BT_NIMBLE_ROLE_CENTRAL + // stop processing if already connected + NimBLEClient* pClient = NimBLEDevice::getClientByPeerAddress(advertisedAddress); + if (pClient != nullptr && pClient->isConnected()) { + NIMBLE_LOGI(LOG_TAG, "Ignoring device: address: %s, already connected", advertisedAddress.toString().c_str()); return 0; } - +# endif NimBLEAdvertisedDevice* advertisedDevice = nullptr; // If we've seen this device before get a pointer to it from the vector - for(auto &it: pScan->m_scanResults.m_advertisedDevicesVector) { -#if CONFIG_BT_NIMBLE_EXT_ADV + for (const auto& dev : pScan->m_scanResults.m_deviceVec) { +# if CONFIG_BT_NIMBLE_EXT_ADV // Same address but different set ID should create a new advertised device. - if (it->getAddress() == advertisedAddress && it->getSetId() == disc.sid) { -#else - if (it->getAddress() == advertisedAddress) { -#endif - advertisedDevice = it; + if (dev->getAddress() == advertisedAddress && dev->getSetId() == disc.sid) +# else + if (dev->getAddress() == advertisedAddress) +# endif + { + advertisedDevice = dev; break; } } // If we haven't seen this device before; create a new instance and insert it in the vector. // Otherwise just update the relevant parameters of the already known device. - if (advertisedDevice == nullptr && - (!isLegacyAdv || event_type != BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP)) { + if (advertisedDevice == nullptr) { // Check if we have reach the scan results limit, ignore this one if so. // We still need to store each device when maxResults is 0 to be able to append the scan results if (pScan->m_maxResults > 0 && pScan->m_maxResults < 0xFF && - (pScan->m_scanResults.m_advertisedDevicesVector.size() >= pScan->m_maxResults)) { + (pScan->m_scanResults.m_deviceVec.size() >= pScan->m_maxResults)) { return 0; } - advertisedDevice = new NimBLEAdvertisedDevice(); - advertisedDevice->setAddress(advertisedAddress); - advertisedDevice->setAdvType(event_type, isLegacyAdv); -#if CONFIG_BT_NIMBLE_EXT_ADV - advertisedDevice->setSetId(disc.sid); - advertisedDevice->setPrimaryPhy(disc.prim_phy); - advertisedDevice->setSecondaryPhy(disc.sec_phy); - advertisedDevice->setPeriodicInterval(disc.periodic_adv_itvl); -#endif - pScan->m_scanResults.m_advertisedDevicesVector.push_back(advertisedDevice); + if (isLegacyAdv && event_type == BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP) { + NIMBLE_LOGI(LOG_TAG, "Scan response without advertisement: %s", advertisedAddress.toString().c_str()); + } + + advertisedDevice = new NimBLEAdvertisedDevice(event, event_type); + pScan->m_scanResults.m_deviceVec.push_back(advertisedDevice); NIMBLE_LOGI(LOG_TAG, "New advertiser: %s", advertisedAddress.toString().c_str()); - } else if (advertisedDevice != nullptr) { - NIMBLE_LOGI(LOG_TAG, "Updated advertiser: %s", advertisedAddress.toString().c_str()); } else { - // Scan response from unknown device - return 0; + advertisedDevice->update(event, event_type); + if (isLegacyAdv && event_type == BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP) { + NIMBLE_LOGI(LOG_TAG, "Scan response from: %s", advertisedAddress.toString().c_str()); + } else { + NIMBLE_LOGI(LOG_TAG, "Duplicate; updated: %s", advertisedAddress.toString().c_str()); + } } - advertisedDevice->m_timestamp = time(nullptr); - advertisedDevice->setRSSI(disc.rssi); - advertisedDevice->setPayload(disc.data, disc.length_data, (isLegacyAdv && - event_type == BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP)); + if (!advertisedDevice->m_callbackSent) { + advertisedDevice->m_callbackSent++; + pScan->m_pScanCallbacks->onDiscovered(advertisedDevice); + } - if (pScan->m_pScanCallbacks) { - if (advertisedDevice->m_callbackSent == 0 || !pScan->m_scan_params.filter_duplicates) { - advertisedDevice->m_callbackSent = 1; - pScan->m_pScanCallbacks->onDiscovered(advertisedDevice); - } + // If not active scanning or scan response is not available + // or extended advertisement scanning, report the result to the callback now. + if (pScan->m_scanParams.passive || !isLegacyAdv || !advertisedDevice->isScannable()) { + advertisedDevice->m_callbackSent++; + pScan->m_pScanCallbacks->onResult(advertisedDevice); + } else if (isLegacyAdv && event_type == BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP) { + advertisedDevice->m_callbackSent++; + // got the scan response report the full data. + pScan->m_pScanCallbacks->onResult(advertisedDevice); + } - if (pScan->m_scan_params.filter_duplicates && advertisedDevice->m_callbackSent >= 2) { - return 0; - } - - // If not active scanning or scan response is not available - // or extended advertisement scanning, report the result to the callback now. - if(pScan->m_scan_params.passive || !isLegacyAdv || - (advertisedDevice->getAdvType() != BLE_HCI_ADV_TYPE_ADV_IND && - advertisedDevice->getAdvType() != BLE_HCI_ADV_TYPE_ADV_SCAN_IND)) - { - advertisedDevice->m_callbackSent = 2; - pScan->m_pScanCallbacks->onResult(advertisedDevice); - - // Otherwise, wait for the scan response so we can report the complete data. - } else if (isLegacyAdv && event_type == BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP) { - advertisedDevice->m_callbackSent = 2; - pScan->m_pScanCallbacks->onResult(advertisedDevice); - } - // If not storing results and we have invoked the callback, delete the device. - if(pScan->m_maxResults == 0 && advertisedDevice->m_callbackSent >= 2) { - pScan->erase(advertisedAddress); - } + // If not storing results and we have invoked the callback, delete the device. + if (pScan->m_maxResults == 0 && advertisedDevice->m_callbackSent >= 2) { + pScan->erase(advertisedDevice); } return 0; } - case BLE_GAP_EVENT_DISC_COMPLETE: { - NIMBLE_LOGD(LOG_TAG, "discovery complete; reason=%d", - event->disc_complete.reason); - if(pScan->m_maxResults == 0) { + case BLE_GAP_EVENT_DISC_COMPLETE: { + NIMBLE_LOGD(LOG_TAG, "discovery complete; reason=%d", event->disc_complete.reason); + + if (pScan->m_maxResults == 0) { pScan->clearResults(); } - if (pScan->m_pScanCallbacks != nullptr) { - pScan->m_pScanCallbacks->onScanEnd(pScan->m_scanResults); - } + pScan->m_pScanCallbacks->onScanEnd(pScan->m_scanResults, event->disc_complete.reason); - if(pScan->m_pTaskData != nullptr) { - pScan->m_pTaskData->rc = event->disc_complete.reason; - xTaskNotifyGive(pScan->m_pTaskData->task); + if (pScan->m_pTaskData != nullptr) { + NimBLEUtils::taskRelease(*pScan->m_pTaskData, event->disc_complete.reason); } return 0; @@ -189,8 +167,7 @@ int NimBLEScan::handleGapEvent(ble_gap_event* event, void* arg) { default: return 0; } -} // gapEventHandler - +} // handleGapEvent /** * @brief Should we perform an active or passive scan? @@ -198,32 +175,32 @@ int NimBLEScan::handleGapEvent(ble_gap_event* event, void* arg) { * @param [in] active If true, we perform an active scan otherwise a passive scan. */ void NimBLEScan::setActiveScan(bool active) { - m_scan_params.passive = !active; + m_scanParams.passive = !active; } // setActiveScan - /** * @brief Set whether or not the BLE controller should only report results * from devices it has not already seen. - * @param [in] enabled If true, scanned devices will only be reported once. - * @details The controller has a limited buffer and will start reporting - * duplicate devices once the limit is reached. + * @param [in] enabled If set to 1 (true), scanned devices will only be reported once. + * If set to 0 duplicates will be reported each time they are seen. + * If using extended scanning this can be set to 2 which will reset the duplicate filter + * at the end of each scan period if the scan period is set. + * @note The controller has a limited buffer and will start reporting +duplicate devices once the limit is reached. */ -void NimBLEScan::setDuplicateFilter(bool enabled) { - m_scan_params.filter_duplicates = enabled; +void NimBLEScan::setDuplicateFilter(uint8_t enabled) { + m_scanParams.filter_duplicates = enabled; } // setDuplicateFilter - /** - * @brief Set whether or not the BLE controller only report scan results - * from devices advertising in limited discovery mode, i.e. directed advertising. + * @brief Set whether or not the BLE controller only reports scan results + * from devices advertising in limited discovery mode. * @param [in] enabled If true, only limited discovery devices will be in scan results. */ void NimBLEScan::setLimitedOnly(bool enabled) { - m_scan_params.limited = enabled; + m_scanParams.limited = enabled; } // setLimited - /** * @brief Sets the scan filter policy. * @param [in] filter Can be one of: @@ -243,10 +220,9 @@ void NimBLEScan::setLimitedOnly(bool enabled) { * resolvable private address. */ void NimBLEScan::setFilterPolicy(uint8_t filter) { - m_scan_params.filter_policy = filter; + m_scanParams.filter_policy = filter; } // setFilterPolicy - /** * @brief Sets the max number of results to store. * @param [in] maxResults The number of results to limit storage to\n @@ -254,38 +230,40 @@ void NimBLEScan::setFilterPolicy(uint8_t filter) { */ void NimBLEScan::setMaxResults(uint8_t maxResults) { m_maxResults = maxResults; -} - +} // setMaxResults /** * @brief Set the call backs to be invoked. * @param [in] pScanCallbacks Call backs to be invoked. - * @param [in] wantDuplicates True if we wish to be called back with duplicates. Default is false. + * @param [in] wantDuplicates True if we wish to be called back with duplicates, default: false. */ void NimBLEScan::setScanCallbacks(NimBLEScanCallbacks* pScanCallbacks, bool wantDuplicates) { setDuplicateFilter(!wantDuplicates); + if (pScanCallbacks == nullptr) { + m_pScanCallbacks = &defaultScanCallbacks; + return; + } m_pScanCallbacks = pScanCallbacks; } // setScanCallbacks - /** * @brief Set the interval to scan. - * @param [in] intervalMSecs The scan interval (how often) in milliseconds. + * @param [in] intervalMs The scan interval in milliseconds. + * @details The interval is the time between the start of two consecutive scan windows. + * When a new interval starts the controller changes the channel it's scanning on. */ -void NimBLEScan::setInterval(uint16_t intervalMSecs) { - m_scan_params.itvl = intervalMSecs / 0.625; +void NimBLEScan::setInterval(uint16_t intervalMs) { + m_scanParams.itvl = (intervalMs * 16) / 10; } // setInterval - /** * @brief Set the window to actively scan. - * @param [in] windowMSecs How long to actively scan. + * @param [in] windowMs How long during the interval to actively scan in milliseconds. */ -void NimBLEScan::setWindow(uint16_t windowMSecs) { - m_scan_params.window = windowMSecs / 0.625; +void NimBLEScan::setWindow(uint16_t windowMs) { + m_scanParams.window = (windowMs * 16) / 10; } // setWindow - /** * @brief Get the status of the scanner. * @return true if scanning or scan starting. @@ -294,61 +272,84 @@ bool NimBLEScan::isScanning() { return ble_gap_disc_active(); } +# if CONFIG_BT_NIMBLE_EXT_ADV +/** + * @brief Set the PHYs to scan. + * @param [in] phyMask The PHYs to scan, a bit mask of: + * * NIMBLE_CPP_SCAN_1M + * * NIMBLE_CPP_SCAN_CODED + */ +void NimBLEScan::setPhy(Phy phyMask) { + m_phy = phyMask; +} // setScanPhy + +/** + * @brief Set the extended scanning period. + * @param [in] periodMs The scan period in milliseconds + * @details The period is the time between the start of two consecutive scan periods. + * This works as a timer to restart scanning at the specified amount of time in periodMs. + * @note The duration used when this is set must be less than period. + */ +void NimBLEScan::setPeriod(uint32_t periodMs) { + m_period = (periodMs + 500) / 1280; // round up 1.28 second units +} // setScanPeriod +# endif /** * @brief Start scanning. - * @param [in] duration The duration in milliseconds for which to scan. - * @param [in] is_continue Set to true to save previous scan results, false to clear them. + * @param [in] duration The duration in milliseconds for which to scan. 0 == scan forever. + * @param [in] isContinue Set to true to save previous scan results, false to clear them. + * @param [in] restart Set to true to restart the scan if already in progress. + * this is useful to clear the duplicate filter so all devices are reported again. * @return True if scan started or false if there was an error. */ -bool NimBLEScan::start(uint32_t duration, bool is_continue) { +bool NimBLEScan::start(uint32_t duration, bool isContinue, bool restart) { NIMBLE_LOGD(LOG_TAG, ">> start: duration=%" PRIu32, duration); + if (isScanning()) { + if (restart) { + NIMBLE_LOGI(LOG_TAG, "Scan already in progress, restarting it"); + if (!stop()) { + return false; + } - // Save the duration in the case that the host is reset so we can reuse it. - m_duration = duration; - - // If 0 duration specified then we assume a continuous scan is desired. - if(duration == 0){ - duration = BLE_HS_FOREVER; + if (!isContinue) { + clearResults(); + } + } + } else { // Don't clear results while scanning is active + if (!isContinue) { + clearResults(); + } } - // Set the flag to ignore the results while we are deleting the vector - if(!is_continue) { - m_ignoreResults = true; - } + // If scanning is already active, call the functions anyway as the parameters can be changed. # if CONFIG_BT_NIMBLE_EXT_ADV ble_gap_ext_disc_params scan_params; - scan_params.passive = m_scan_params.passive; - scan_params.itvl = m_scan_params.itvl; - scan_params.window = m_scan_params.window; - int rc = ble_gap_ext_disc(NimBLEDevice::m_own_addr_type, - duration/10, - 0, - m_scan_params.filter_duplicates, - m_scan_params.filter_policy, - m_scan_params.limited, - &scan_params, - &scan_params, + scan_params.passive = m_scanParams.passive; + scan_params.itvl = m_scanParams.itvl; + scan_params.window = m_scanParams.window; + int rc = ble_gap_ext_disc(NimBLEDevice::m_ownAddrType, + duration / 10, // 10ms units + m_period, + m_scanParams.filter_duplicates, + m_scanParams.filter_policy, + m_scanParams.limited, + m_phy & SCAN_1M ? &scan_params : NULL, + m_phy & SCAN_CODED ? &scan_params : NULL, NimBLEScan::handleGapEvent, NULL); -#else - int rc = ble_gap_disc(NimBLEDevice::m_own_addr_type, - duration, - &m_scan_params, +# else + int rc = ble_gap_disc(NimBLEDevice::m_ownAddrType, + duration ? duration : BLE_HS_FOREVER, + &m_scanParams, NimBLEScan::handleGapEvent, NULL); -#endif - switch(rc) { +# endif + switch (rc) { case 0: - if(!is_continue) { - clearResults(); - } - break; - case BLE_HS_EALREADY: - // Clear the cache if already scanning in case an advertiser was missed. - clearDuplicateCache(); + NIMBLE_LOGD(LOG_TAG, "Scan started"); break; case BLE_HS_EBUSY: @@ -363,21 +364,14 @@ bool NimBLEScan::start(uint32_t duration, bool is_continue) { break; default: - NIMBLE_LOGE(LOG_TAG, "Error initiating GAP discovery procedure; rc=%d, %s", - rc, NimBLEUtils::returnCodeToString(rc)); + NIMBLE_LOGE(LOG_TAG, "Error starting scan; rc=%d, %s", rc, NimBLEUtils::returnCodeToString(rc)); break; } - m_ignoreResults = false; NIMBLE_LOGD(LOG_TAG, "<< start()"); - - if(rc != 0 && rc != BLE_HS_EALREADY) { - return false; - } - return true; + return rc == 0 || rc == BLE_HS_EALREADY; } // start - /** * @brief Stop an in progress scan. * @return True if successful. @@ -391,72 +385,56 @@ bool NimBLEScan::stop() { return false; } - if(m_maxResults == 0) { + if (m_maxResults == 0) { clearResults(); } - if (rc != BLE_HS_EALREADY && m_pScanCallbacks != nullptr) { - m_pScanCallbacks->onScanEnd(m_scanResults); - } - - if(m_pTaskData != nullptr) { - xTaskNotifyGive(m_pTaskData->task); + if (m_pTaskData != nullptr) { + NimBLEUtils::taskRelease(*m_pTaskData); } NIMBLE_LOGD(LOG_TAG, "<< stop()"); return true; } // stop - -/** - * @brief Clears the duplicate scan filter cache. - */ -void NimBLEScan::clearDuplicateCache() { -#ifdef CONFIG_IDF_TARGET_ESP32 // Not available for ESP32C3 - esp_ble_scan_dupilcate_list_flush(); -#endif -} - - /** * @brief Delete peer device from the scan results vector. * @param [in] address The address of the device to delete from the results. - * @details After disconnecting, it may be required in the case we were connected to a device without a public address. */ -void NimBLEScan::erase(const NimBLEAddress &address) { +void NimBLEScan::erase(const NimBLEAddress& address) { NIMBLE_LOGD(LOG_TAG, "erase device: %s", address.toString().c_str()); - - for(auto it = m_scanResults.m_advertisedDevicesVector.begin(); it != m_scanResults.m_advertisedDevicesVector.end(); ++it) { - if((*it)->getAddress() == address) { + for (auto it = m_scanResults.m_deviceVec.begin(); it != m_scanResults.m_deviceVec.end(); ++it) { + if ((*it)->getAddress() == address) { delete *it; - m_scanResults.m_advertisedDevicesVector.erase(it); + m_scanResults.m_deviceVec.erase(it); break; } } } - /** - * @brief Called when host reset, we set a flag to stop scanning until synced. + * @brief Delete peer device from the scan results vector. + * @param [in] device The device to delete from the results. */ -void NimBLEScan::onHostReset() { - m_ignoreResults = true; +void NimBLEScan::erase(const NimBLEAdvertisedDevice* device) { + NIMBLE_LOGD(LOG_TAG, "erase device: %s", device->getAddress().toString().c_str()); + for (auto it = m_scanResults.m_deviceVec.begin(); it != m_scanResults.m_deviceVec.end(); ++it) { + if ((*it) == device) { + delete *it; + m_scanResults.m_deviceVec.erase(it); + break; + } + } } - /** * @brief If the host reset and re-synced this is called. * If the application was scanning indefinitely with a callback, restart it. */ void NimBLEScan::onHostSync() { - m_ignoreResults = false; - - if(m_duration == 0 && m_pScanCallbacks != nullptr) { - start(0, false); - } + m_pScanCallbacks->onScanEnd(m_scanResults, BLE_HS_ENOTSYNCED); } - /** * @brief Start scanning and block until scanning has been completed. * @param [in] duration The duration in milliseconds for which to scan. @@ -464,27 +442,26 @@ void NimBLEScan::onHostSync() { * @return The scan results. */ NimBLEScanResults NimBLEScan::getResults(uint32_t duration, bool is_continue) { - if(duration == 0) { + if (duration == 0) { NIMBLE_LOGW(LOG_TAG, "Blocking scan called with duration = forever"); } - TaskHandle_t cur_task = xTaskGetCurrentTaskHandle(); - ble_task_data_t taskData = {nullptr, cur_task, 0, nullptr}; + if (m_pTaskData != nullptr) { + NIMBLE_LOGE(LOG_TAG, "Scan already in progress"); + return m_scanResults; + } + + NimBLETaskData taskData; m_pTaskData = &taskData; - if(start(duration, is_continue)) { -#ifdef ulTaskNotifyValueClear - // Clear the task notification value to ensure we block - ulTaskNotifyValueClear(cur_task, ULONG_MAX); -#endif - ulTaskNotifyTake(pdTRUE, portMAX_DELAY); + if (start(duration, is_continue)) { + NimBLEUtils::taskWait(taskData, BLE_NPL_TIME_FOREVER); } m_pTaskData = nullptr; return m_scanResults; } // getResults - /** * @brief Get the results of the scan. * @return NimBLEScanResults object. @@ -493,82 +470,94 @@ NimBLEScanResults NimBLEScan::getResults() { return m_scanResults; } - /** - * @brief Clear the results of the scan. + * @brief Clear the stored results of the scan. */ void NimBLEScan::clearResults() { - for(auto &it: m_scanResults.m_advertisedDevicesVector) { - delete it; + if (m_scanResults.m_deviceVec.size()) { + std::vector vSwap{}; + ble_npl_hw_enter_critical(); + vSwap.swap(m_scanResults.m_deviceVec); + ble_npl_hw_exit_critical(0); + for (const auto& dev : vSwap) { + delete dev; + } } - m_scanResults.m_advertisedDevicesVector.clear(); - clearDuplicateCache(); -} - +} // clearResults /** * @brief Dump the scan results to the log. */ -void NimBLEScanResults::dump() { - NIMBLE_LOGD(LOG_TAG, ">> Dump scan results:"); - for (int i=0; i= 3 + for (const auto& dev : m_deviceVec) { + NIMBLE_LOGI(LOG_TAG, "- %s", dev->toString().c_str()); } +# endif } // dump - /** * @brief Get the count of devices found in the last scan. * @return The number of devices found in the last scan. */ -int NimBLEScanResults::getCount() { - return m_advertisedDevicesVector.size(); +int NimBLEScanResults::getCount() const { + return m_deviceVec.size(); } // getCount - /** * @brief Return the specified device at the given index. * The index should be between 0 and getCount()-1. - * @param [in] i The index of the device. + * @param [in] idx The index of the device. * @return The device at the specified index. */ -NimBLEAdvertisedDevice NimBLEScanResults::getDevice(uint32_t i) { - return *m_advertisedDevicesVector[i]; +const NimBLEAdvertisedDevice* NimBLEScanResults::getDevice(uint32_t idx) const { + return m_deviceVec[idx]; } - /** * @brief Get iterator to the beginning of the vector of advertised device pointers. * @return An iterator to the beginning of the vector of advertised device pointers. */ -std::vector::iterator NimBLEScanResults::begin() { - return m_advertisedDevicesVector.begin(); +std::vector::const_iterator NimBLEScanResults::begin() const { + return m_deviceVec.begin(); } - /** * @brief Get iterator to the end of the vector of advertised device pointers. * @return An iterator to the end of the vector of advertised device pointers. */ -std::vector::iterator NimBLEScanResults::end() { - return m_advertisedDevicesVector.end(); +std::vector::const_iterator NimBLEScanResults::end() const { + return m_deviceVec.end(); } - /** * @brief Get a pointer to the specified device at the given address. * If the address is not found a nullptr is returned. * @param [in] address The address of the device. * @return A pointer to the device at the specified address. */ -NimBLEAdvertisedDevice *NimBLEScanResults::getDevice(const NimBLEAddress &address) { - for(size_t index = 0; index < m_advertisedDevicesVector.size(); index++) { - if(m_advertisedDevicesVector[index]->getAddress() == address) { - return m_advertisedDevicesVector[index]; +const NimBLEAdvertisedDevice* NimBLEScanResults::getDevice(const NimBLEAddress& address) const { + for (const auto& dev : m_deviceVec) { + if (dev->getAddress() == address) { + return dev; } } return nullptr; } -#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_OBSERVER */ +static const char* CB_TAG = "NimBLEScanCallbacks"; + +void NimBLEScanCallbacks::onDiscovered(const NimBLEAdvertisedDevice* pAdvertisedDevice) { + NIMBLE_LOGD(CB_TAG, "Discovered: %s", pAdvertisedDevice->toString().c_str()); +} + +void NimBLEScanCallbacks::onResult(const NimBLEAdvertisedDevice* pAdvertisedDevice) { + NIMBLE_LOGD(CB_TAG, "Result: %s", pAdvertisedDevice->toString().c_str()); +} + +void NimBLEScanCallbacks::onScanEnd(const NimBLEScanResults& results, int reason) { + NIMBLE_LOGD(CB_TAG, "Scan ended; reason %d, num results: %d", reason, results.getCount()); +} + +#endif // CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_OBSERVER diff --git a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEScan.h b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEScan.h index f0edcaa94..7884d190f 100644 --- a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEScan.h +++ b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEScan.h @@ -1,32 +1,36 @@ /* - * NimBLEScan.h + * Copyright 2020-2025 Ryan Powell and + * esp-nimble-cpp, NimBLE-Arduino contributors. * - * Created: on Jan 24 2020 - * Author H2zero + * 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 * - * Originally: + * http://www.apache.org/licenses/LICENSE-2.0 * - * BLEScan.h - * - * Created on: Jul 1, 2017 - * Author: kolban + * 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 COMPONENTS_NIMBLE_SCAN_H_ -#define COMPONENTS_NIMBLE_SCAN_H_ + +#ifndef NIMBLE_CPP_SCAN_H_ +#define NIMBLE_CPP_SCAN_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 "NimBLEAdvertisedDevice.h" -#include "NimBLEUtils.h" +# include "NimBLEAdvertisedDevice.h" +# include "NimBLEUtils.h" -#if defined(CONFIG_NIMBLE_CPP_IDF) -#include "host/ble_gap.h" -#else -#include "nimble/nimble/host/include/host/ble_gap.h" -#endif +# if defined(CONFIG_NIMBLE_CPP_IDF) +# include "host/ble_gap.h" +# else +# include "nimble/nimble/host/include/host/ble_gap.h" +# endif -#include +# include class NimBLEDevice; class NimBLEScan; @@ -42,17 +46,17 @@ class NimBLEAddress; * index (starting at 0) of the desired device. */ class NimBLEScanResults { -public: - void dump(); - int getCount(); - NimBLEAdvertisedDevice getDevice(uint32_t i); - std::vector::iterator begin(); - std::vector::iterator end(); - NimBLEAdvertisedDevice *getDevice(const NimBLEAddress &address); + public: + void dump() const; + int getCount() const; + const NimBLEAdvertisedDevice* getDevice(uint32_t idx) const; + const NimBLEAdvertisedDevice* getDevice(const NimBLEAddress& address) const; + std::vector::const_iterator begin() const; + std::vector::const_iterator end() const; -private: + private: friend NimBLEScan; - std::vector m_advertisedDevicesVector; + std::vector m_deviceVec; }; /** @@ -61,68 +65,76 @@ private: * Scanning is associated with a %BLE client that is attempting to locate BLE servers. */ class NimBLEScan { -public: - bool start(uint32_t duration, bool is_continue = false); - bool isScanning(); - void setScanCallbacks(NimBLEScanCallbacks* pScanCallbacks, bool wantDuplicates = false); - void setActiveScan(bool active); - void setInterval(uint16_t intervalMSecs); - void setWindow(uint16_t windowMSecs); - void setDuplicateFilter(bool enabled); - void setLimitedOnly(bool enabled); - void setFilterPolicy(uint8_t filter); - void clearDuplicateCache(); - bool stop(); - void clearResults(); - NimBLEScanResults getResults(); - NimBLEScanResults getResults(uint32_t duration, bool is_continue = false); - void setMaxResults(uint8_t maxResults); - void erase(const NimBLEAddress &address); + public: + bool start(uint32_t duration, bool isContinue = false, bool restart = true); + bool isScanning(); + void setScanCallbacks(NimBLEScanCallbacks* pScanCallbacks, bool wantDuplicates = false); + void setActiveScan(bool active); + void setInterval(uint16_t intervalMs); + void setWindow(uint16_t windowMs); + void setDuplicateFilter(uint8_t enabled); + void setLimitedOnly(bool enabled); + void setFilterPolicy(uint8_t filter); + bool stop(); + void clearResults(); + NimBLEScanResults getResults(); + NimBLEScanResults getResults(uint32_t duration, bool is_continue = false); + void setMaxResults(uint8_t maxResults); + void erase(const NimBLEAddress& address); + void erase(const NimBLEAdvertisedDevice* device); +# if CONFIG_BT_NIMBLE_EXT_ADV + enum Phy { SCAN_1M = 0x01, SCAN_CODED = 0x02, SCAN_ALL = 0x03 }; + void setPhy(Phy phyMask); + void setPeriod(uint32_t periodMs); +# endif -private: + private: friend class NimBLEDevice; NimBLEScan(); ~NimBLEScan(); - static int handleGapEvent(ble_gap_event* event, void* arg); - void onHostReset(); - void onHostSync(); + static int handleGapEvent(ble_gap_event* event, void* arg); + void onHostSync(); - NimBLEScanCallbacks* m_pScanCallbacks; - ble_gap_disc_params m_scan_params; - bool m_ignoreResults; - NimBLEScanResults m_scanResults; - uint32_t m_duration; - ble_task_data_t *m_pTaskData; - uint8_t m_maxResults; + NimBLEScanCallbacks* m_pScanCallbacks; + ble_gap_disc_params m_scanParams; + NimBLEScanResults m_scanResults; + NimBLETaskData* m_pTaskData; + uint8_t m_maxResults; + +# if CONFIG_BT_NIMBLE_EXT_ADV + uint8_t m_phy{SCAN_ALL}; + uint16_t m_period{0}; +# endif }; /** * @brief A callback handler for callbacks associated device scanning. */ class NimBLEScanCallbacks { -public: + public: virtual ~NimBLEScanCallbacks() {} /** * @brief Called when a new device is discovered, before the scan result is received (if applicable). * @param [in] advertisedDevice The device which was discovered. */ - virtual void onDiscovered(NimBLEAdvertisedDevice* advertisedDevice) {}; + virtual void onDiscovered(const NimBLEAdvertisedDevice* advertisedDevice); /** * @brief Called when a new scan result is complete, including scan response data (if applicable). * @param [in] advertisedDevice The device for which the complete result is available. */ - virtual void onResult(NimBLEAdvertisedDevice* advertisedDevice) {}; + virtual void onResult(const NimBLEAdvertisedDevice* advertisedDevice); /** * @brief Called when a scan operation ends. * @param [in] scanResults The results of the scan that ended. + * @param [in] reason The reason code for why the scan ended. */ - virtual void onScanEnd(NimBLEScanResults scanResults) {}; + virtual void onScanEnd(const NimBLEScanResults& scanResults, int reason); }; -#endif /* CONFIG_BT_ENABLED CONFIG_BT_NIMBLE_ROLE_OBSERVER */ -#endif /* COMPONENTS_NIMBLE_SCAN_H_ */ +#endif // CONFIG_BT_ENABLED CONFIG_BT_NIMBLE_ROLE_OBSERVER +#endif // NIMBLE_CPP_SCAN_H_ diff --git a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEServer.cpp b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEServer.cpp index 15f933c3f..42eb930d8 100644 --- a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEServer.cpp +++ b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEServer.cpp @@ -1,193 +1,186 @@ /* - * NimBLEServer.cpp + * Copyright 2020-2025 Ryan Powell and + * esp-nimble-cpp, NimBLE-Arduino contributors. * - * Created: on March 2, 2020 - * Author H2zero + * 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 * - * Originally: + * http://www.apache.org/licenses/LICENSE-2.0 * - * BLEServer.cpp - * - * Created on: Apr 16, 2017 - * Author: kolban + * 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 "nimconfig.h" -#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) - #include "NimBLEServer.h" -#include "NimBLEDevice.h" -#include "NimBLELog.h" +#if CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL -#if defined(CONFIG_NIMBLE_CPP_IDF) -#include "services/gap/ble_svc_gap.h" -#include "services/gatt/ble_svc_gatt.h" -#else -#include "nimble/nimble/host/services/gap/include/services/gap/ble_svc_gap.h" -#include "nimble/nimble/host/services/gatt/include/services/gatt/ble_svc_gatt.h" -#endif +# include "NimBLEDevice.h" +# include "NimBLELog.h" -#include +# if CONFIG_BT_NIMBLE_ROLE_CENTRAL +# include "NimBLEClient.h" +# endif -#define NIMBLE_SERVER_GET_PEER_NAME_ON_CONNECT_CB 0 -#define NIMBLE_SERVER_GET_PEER_NAME_ON_AUTH_CB 1 +# if defined(CONFIG_NIMBLE_CPP_IDF) +# include "services/gap/ble_svc_gap.h" +# include "services/gatt/ble_svc_gatt.h" +# else +# include "nimble/nimble/host/services/gap/include/services/gap/ble_svc_gap.h" +# include "nimble/nimble/host/services/gatt/include/services/gatt/ble_svc_gatt.h" +# endif -static const char* LOG_TAG = "NimBLEServer"; +# define NIMBLE_SERVER_GET_PEER_NAME_ON_CONNECT_CB 0 +# define NIMBLE_SERVER_GET_PEER_NAME_ON_AUTH_CB 1 + +static const char* LOG_TAG = "NimBLEServer"; static NimBLEServerCallbacks defaultCallbacks; - /** - * @brief Construct a %BLE Server + * @brief Construct a BLE Server * - * This class is not designed to be individually instantiated. Instead one should create a server by asking - * the NimBLEDevice class. + * This class is not designed to be individually instantiated. + * Instead it should be created the NimBLEDevice API. */ -NimBLEServer::NimBLEServer() { - memset(m_indWait, BLE_HS_CONN_HANDLE_NONE, sizeof(m_indWait)); -// m_svcChgChrHdl = 0xffff; // Future Use - m_pServerCallbacks = &defaultCallbacks; - m_gattsStarted = false; -#if !CONFIG_BT_NIMBLE_EXT_ADV - m_advertiseOnDisconnect = true; -#endif - m_svcChanged = false; - m_deleteCallbacks = true; - m_getPeerNameOnConnect = false; +NimBLEServer::NimBLEServer() + : m_gattsStarted{false}, + m_svcChanged{false}, + m_deleteCallbacks{false}, +# if !CONFIG_BT_NIMBLE_EXT_ADV + m_advertiseOnDisconnect{false}, +# endif + m_pServerCallbacks{&defaultCallbacks}, + m_svcVec{} { + m_connectedPeers.fill(BLE_HS_CONN_HANDLE_NONE); } // NimBLEServer - /** * @brief Destructor: frees all resources / attributes created. */ NimBLEServer::~NimBLEServer() { - for(auto &it : m_svcVec) { - delete it; + for (const auto& svc : m_svcVec) { + delete svc; } - if(m_deleteCallbacks && m_pServerCallbacks != &defaultCallbacks) { + if (m_deleteCallbacks) { delete m_pServerCallbacks; } + +# if CONFIG_BT_NIMBLE_ROLE_CENTRAL + if (m_pClient != nullptr) { + delete m_pClient; + } +# endif } - /** - * @brief Create a %BLE Service. + * @brief Create a BLE Service. * @param [in] uuid The UUID of the new service. - * @return A reference to the new service object. + * @return A pointer to the new service object. */ NimBLEService* NimBLEServer::createService(const char* uuid) { return createService(NimBLEUUID(uuid)); } // createService - /** - * @brief Create a %BLE Service. + * @brief Create a BLE Service. * @param [in] uuid The UUID of the new service. - * @return A reference to the new service object. + * @return A pointer to the new service object. */ -NimBLEService* NimBLEServer::createService(const NimBLEUUID &uuid) { - NIMBLE_LOGD(LOG_TAG, ">> createService - %s", uuid.toString().c_str()); - - // Check that a service with the supplied UUID does not already exist. - if(getServiceByUUID(uuid) != nullptr) { - NIMBLE_LOGW(LOG_TAG, "Warning creating a duplicate service UUID: %s", - std::string(uuid).c_str()); - } - +NimBLEService* NimBLEServer::createService(const NimBLEUUID& uuid) { NimBLEService* pService = new NimBLEService(uuid); m_svcVec.push_back(pService); serviceChanged(); - NIMBLE_LOGD(LOG_TAG, "<< createService"); return pService; } // createService - /** - * @brief Get a %BLE Service by its UUID + * @brief Get a BLE Service by its UUID * @param [in] uuid The UUID of the service. * @param instanceId The index of the service to return (used when multiple services have the same UUID). * @return A pointer to the service object or nullptr if not found. */ -NimBLEService* NimBLEServer::getServiceByUUID(const char* uuid, uint16_t instanceId) { +NimBLEService* NimBLEServer::getServiceByUUID(const char* uuid, uint16_t instanceId) const { return getServiceByUUID(NimBLEUUID(uuid), instanceId); } // getServiceByUUID - /** - * @brief Get a %BLE Service by its UUID + * @brief Get a BLE Service by its UUID * @param [in] uuid The UUID of the service. * @param instanceId The index of the service to return (used when multiple services have the same UUID). * @return A pointer to the service object or nullptr if not found. */ -NimBLEService* NimBLEServer::getServiceByUUID(const NimBLEUUID &uuid, uint16_t instanceId) { +NimBLEService* NimBLEServer::getServiceByUUID(const NimBLEUUID& uuid, uint16_t instanceId) const { uint16_t position = 0; - for (auto &it : m_svcVec) { - if (it->getUUID() == uuid) { - if (position == instanceId){ - return it; + for (const auto& svc : m_svcVec) { + if (svc->getUUID() == uuid) { + if (position == instanceId) { + return svc; } position++; } } + return nullptr; } // getServiceByUUID /** - * @brief Get a %BLE Service by its handle + * @brief Get a BLE Service by its handle * @param handle The handle of the service. * @return A pointer to the service object or nullptr if not found. */ -NimBLEService *NimBLEServer::getServiceByHandle(uint16_t handle) { - for (auto &it : m_svcVec) { - if (it->getHandle() == handle) { - return it; +NimBLEService* NimBLEServer::getServiceByHandle(uint16_t handle) const { + for (const auto& svc : m_svcVec) { + if (svc->getHandle() == handle) { + return svc; } } + return nullptr; } - -#if CONFIG_BT_NIMBLE_EXT_ADV +# if CONFIG_BT_NIMBLE_EXT_ADV /** * @brief Retrieve the advertising object that can be used to advertise the existence of the server. - * @return An advertising object. + * @return A pinter to an advertising object. */ -NimBLEExtAdvertising* NimBLEServer::getAdvertising() { +NimBLEExtAdvertising* NimBLEServer::getAdvertising() const { return NimBLEDevice::getAdvertising(); } // getAdvertising -#endif +# endif -#if !CONFIG_BT_NIMBLE_EXT_ADV || defined(_DOXYGEN_) +# if (!CONFIG_BT_NIMBLE_EXT_ADV && CONFIG_BT_NIMBLE_ROLE_BROADCASTER) || defined(_DOXYGEN_) /** * @brief Retrieve the advertising object that can be used to advertise the existence of the server. - * @return An advertising object. + * @return A pointer to an advertising object. */ -NimBLEAdvertising* NimBLEServer::getAdvertising() { +NimBLEAdvertising* NimBLEServer::getAdvertising() const { return NimBLEDevice::getAdvertising(); } // getAdvertising -#endif +# endif /** - * @brief Sends a service changed notification and resets the GATT server. + * @brief Called when the services are added/removed and sets a flag to indicate they should be reloaded. + * @details This has no effect if the GATT server was not already started. */ void NimBLEServer::serviceChanged() { - if(m_gattsStarted) { + if (m_gattsStarted) { m_svcChanged = true; - ble_svc_gatt_changed(0x0001, 0xffff); - resetGATT(); } -} - +} // serviceChanged /** - * @brief Start the GATT server. Required to be called after setup of all - * services and characteristics / descriptors for the NimBLE host to register them. + * @brief Start the GATT server. + * @details Required to be called after setup of all services and characteristics / descriptors + * for the NimBLE host to register them. */ void NimBLEServer::start() { - if(m_gattsStarted) { - NIMBLE_LOGW(LOG_TAG, "Gatt server already started"); - return; + if (m_gattsStarted) { + return; // already started } int rc = ble_gatts_start(); @@ -196,67 +189,55 @@ void NimBLEServer::start() { return; } -#if CONFIG_NIMBLE_CPP_LOG_LEVEL >= 4 +# if CONFIG_NIMBLE_CPP_LOG_LEVEL >= 4 ble_gatts_show_local(); -#endif -/*** Future use *** - * TODO: implement service changed handling +# endif - ble_uuid16_t svc = {BLE_UUID_TYPE_16, 0x1801}; - ble_uuid16_t chr = {BLE_UUID_TYPE_16, 0x2a05}; - - rc = ble_gatts_find_chr(&svc.u, &chr.u, NULL, &m_svcChgChrHdl); - if(rc != 0) { - NIMBLE_LOGE(LOG_TAG, "ble_gatts_find_chr: rc=%d, %s", rc, - NimBLEUtils::returnCodeToString(rc)); - abort(); - } - - NIMBLE_LOGI(LOG_TAG, "Service changed characterisic handle: %d", m_svcChgChrHdl); -*/ // Get the assigned service handles and build a vector of characteristics // with Notify / Indicate capabilities for event handling - for(auto &svc : m_svcVec) { - if(svc->m_removed == 0) { - rc = ble_gatts_find_svc(&svc->getUUID().getNative()->u, &svc->m_handle); - if(rc != 0) { - NIMBLE_LOGW(LOG_TAG, "GATT Server started without service: %s, Service %s", - svc->getUUID().toString().c_str(), svc->isStarted() ? "missing" : "not started"); + for (const auto& svc : m_svcVec) { + if (svc->getRemoved() == 0) { + rc = ble_gatts_find_svc(svc->getUUID().getBase(), &svc->m_handle); + if (rc != 0) { + NIMBLE_LOGW(LOG_TAG, + "GATT Server started without service: %s, Service %s", + svc->getUUID().toString().c_str(), + svc->isStarted() ? "missing" : "not started"); continue; // Skip this service as it was not started } } - for(auto &chr : svc->m_chrVec) { - // if Notify / Indicate is enabled but we didn't create the descriptor - // we do it now. - if((chr->m_properties & BLE_GATT_CHR_F_INDICATE) || - (chr->m_properties & BLE_GATT_CHR_F_NOTIFY)) { - m_notifyChrVec.push_back(chr); + // Set the descriptor handles now as the stack does not set these when the service is started + for (const auto& chr : svc->m_vChars) { + for (auto& desc : chr->m_vDescriptors) { + ble_gatts_find_dsc(svc->getUUID().getBase(), chr->getUUID().getBase(), desc->getUUID().getBase(), &desc->m_handle); } } } + // If the services have changed indicate it now + if (m_svcChanged) { + m_svcChanged = false; + ble_svc_gatt_changed(0x0001, 0xffff); + } + m_gattsStarted = true; } // start - /** * @brief Disconnect the specified client with optional reason. - * @param [in] connId Connection Id of the client to disconnect. + * @param [in] connHandle Connection handle of the client to disconnect. * @param [in] reason code for disconnecting. - * @return NimBLE host return code. + * @return True if successful. */ -int NimBLEServer::disconnect(uint16_t connId, uint8_t reason) { - NIMBLE_LOGD(LOG_TAG, ">> disconnect()"); - - int rc = ble_gap_terminate(connId, reason); - if(rc != 0){ - NIMBLE_LOGE(LOG_TAG, "ble_gap_terminate failed: rc=%d %s", rc, - NimBLEUtils::returnCodeToString(rc)); +bool NimBLEServer::disconnect(uint16_t connHandle, uint8_t reason) const { + int rc = ble_gap_terminate(connHandle, reason); + if (rc != 0 && rc != BLE_HS_ENOTCONN && rc != BLE_HS_EALREADY) { + NIMBLE_LOGE(LOG_TAG, "ble_gap_terminate failed: rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc)); + return false; } - NIMBLE_LOGD(LOG_TAG, "<< disconnect()"); - return rc; + return true; } // disconnect /** @@ -265,88 +246,96 @@ int NimBLEServer::disconnect(uint16_t connId, uint8_t reason) { * @param [in] reason code for disconnecting. * @return NimBLE host return code. */ -int NimBLEServer::disconnect(const NimBLEConnInfo &connInfo, uint8_t reason) { +bool NimBLEServer::disconnect(const NimBLEConnInfo& connInfo, uint8_t reason) const { return disconnect(connInfo.getConnHandle(), reason); } // disconnect -#if !CONFIG_BT_NIMBLE_EXT_ADV || defined(_DOXYGEN_) +# if !CONFIG_BT_NIMBLE_EXT_ADV || defined(_DOXYGEN_) /** * @brief Set the server to automatically start advertising when a client disconnects. - * @param [in] aod true == advertise, false == don't advertise. + * @param [in] enable true == advertise, false == don't advertise. */ -void NimBLEServer::advertiseOnDisconnect(bool aod) { - m_advertiseOnDisconnect = aod; +void NimBLEServer::advertiseOnDisconnect(bool enable) { + m_advertiseOnDisconnect = enable; } // advertiseOnDisconnect -#endif - -/** - * @brief Set the server to automatically read the name from the connected peer before - * the onConnect callback is called and enables the override callback with name parameter. - * @param [in] enable Enable reading the connected peer name upon connection. - */ -void NimBLEServer::getPeerNameOnConnect(bool enable) { - m_getPeerNameOnConnect = enable; -} // getPeerNameOnConnect +# endif /** * @brief Return the number of connected clients. * @return The number of connected clients. */ -size_t NimBLEServer::getConnectedCount() { - return m_connectedPeersVec.size(); +uint8_t NimBLEServer::getConnectedCount() const { + size_t count = 0; + for (const auto& peer : m_connectedPeers) { + if (peer != BLE_HS_CONN_HANDLE_NONE) { + count++; + } + } + + return count; } // getConnectedCount - /** - * @brief Get the vector of the connected client ID's. + * @brief Get a vector of the connected client handles. + * @return A vector of the connected client handles. */ -std::vector NimBLEServer::getPeerDevices() { - return m_connectedPeersVec; -} // getPeerDevices +std::vector NimBLEServer::getPeerDevices() const { + std::vector peers{}; + for (const auto& peer : m_connectedPeers) { + if (peer != BLE_HS_CONN_HANDLE_NONE) { + peers.push_back(peer); + } + } + return peers; +} // getPeerDevices /** * @brief Get the connection information of a connected peer by vector index. * @param [in] index The vector index of the peer. + * @return A NimBLEConnInfo instance with the peer connection information, or an empty instance if not found. */ -NimBLEConnInfo NimBLEServer::getPeerInfo(size_t index) { - if (index >= m_connectedPeersVec.size()) { - NIMBLE_LOGE(LOG_TAG, "No peer at index %u", index); - return NimBLEConnInfo(); +NimBLEConnInfo NimBLEServer::getPeerInfo(uint8_t index) const { + if (index >= m_connectedPeers.size()) { + NIMBLE_LOGE(LOG_TAG, "Invalid index %u", index); + return NimBLEConnInfo{}; } - return getPeerIDInfo(m_connectedPeersVec[index]); -} // getPeerInfo + auto count = 0; + for (const auto& peer : m_connectedPeers) { + if (peer != BLE_HS_CONN_HANDLE_NONE) { + if (count == index) { + return getPeerInfoByHandle(m_connectedPeers[count]); + } + count++; + } + } + return NimBLEConnInfo{}; +} // getPeerInfo /** * @brief Get the connection information of a connected peer by address. * @param [in] address The address of the peer. + * @return A NimBLEConnInfo instance with the peer connection information, or an empty instance if not found. */ -NimBLEConnInfo NimBLEServer::getPeerInfo(const NimBLEAddress& address) { - ble_addr_t peerAddr; - memcpy(&peerAddr.val, address.getNative(),6); - peerAddr.type = address.getType(); - - NimBLEConnInfo peerInfo; - int rc = ble_gap_conn_find_by_addr(&peerAddr, &peerInfo.m_desc); - if (rc != 0) { +NimBLEConnInfo NimBLEServer::getPeerInfo(const NimBLEAddress& address) const { + NimBLEConnInfo peerInfo{}; + if (ble_gap_conn_find_by_addr(address.getBase(), &peerInfo.m_desc) != 0) { NIMBLE_LOGE(LOG_TAG, "Peer info not found"); } return peerInfo; } // getPeerInfo - /** - * @brief Get the connection information of a connected peer by connection ID. - * @param [in] id The connection id of the peer. + * @brief Get the connection information of a connected peer by connection handle. + * @param [in] connHandle The connection handle of the peer. + * @return A NimBLEConnInfo instance with the peer connection information, or an empty instance if not found. */ -NimBLEConnInfo NimBLEServer::getPeerIDInfo(uint16_t id) { - NimBLEConnInfo peerInfo; - - int rc = ble_gap_conn_find(id, &peerInfo.m_desc); - if (rc != 0) { +NimBLEConnInfo NimBLEServer::getPeerInfoByHandle(uint16_t connHandle) const { + NimBLEConnInfo peerInfo{}; + if (ble_gap_conn_find(connHandle, &peerInfo.m_desc) != 0) { NIMBLE_LOGE(LOG_TAG, "Peer info not found"); } @@ -354,164 +343,45 @@ NimBLEConnInfo NimBLEServer::getPeerIDInfo(uint16_t id) { } // getPeerIDInfo /** - * @brief Callback that is called after reading from the peer name characteristic. - * @details This will check the task pointer in the task data struct to determine - * the action to take once the name has been read. If there is a task waiting then - * it will be woken, if not, the the RC value is checked to determine which callback - * should be called. + * @brief Gap event handler. */ -int NimBLEServer::peerNameCB(uint16_t conn_handle, - const struct ble_gatt_error *error, - struct ble_gatt_attr *attr, - void *arg) { - ble_task_data_t *pTaskData = (ble_task_data_t*)arg; - std::string *name = (std::string*)pTaskData->buf; - int rc = error->status; - - if (rc == 0) { - if (attr) { - name->append(OS_MBUF_DATA(attr->om, char*), OS_MBUF_PKTLEN(attr->om)); - return rc; - } - } - - if (rc == BLE_HS_EDONE) { - // No ask means this was read for a callback. - if (pTaskData->task == nullptr) { - NimBLEServer* pServer = (NimBLEServer*)pTaskData->pATT; - NimBLEConnInfo peerInfo{}; - ble_gap_conn_find(conn_handle, &peerInfo.m_desc); - - // Use the rc value as a flag to indicate which callback should be called. - if (pTaskData->rc == NIMBLE_SERVER_GET_PEER_NAME_ON_CONNECT_CB) { - pServer->m_pServerCallbacks->onConnect(pServer, peerInfo, *name); - } else if (pTaskData->rc == NIMBLE_SERVER_GET_PEER_NAME_ON_AUTH_CB) { - pServer->m_pServerCallbacks->onAuthenticationComplete(peerInfo, *name); - } - } - } else { - NIMBLE_LOGE(LOG_TAG, "NimBLEServerPeerNameCB rc=%d; %s", rc, NimBLEUtils::returnCodeToString(rc)); - } - - if (pTaskData->task != nullptr) { - pTaskData->rc = rc; - xTaskNotifyGive(pTaskData->task); - } else { - // If the read was triggered for callback use then these were allocated. - delete name; - delete pTaskData; - } - - return rc; -} - -/** - * @brief Internal method that sends the read command. - */ -std::string NimBLEServer::getPeerNameInternal(uint16_t conn_handle, TaskHandle_t task, int cb_type) { - std::string *buf = new std::string{}; - ble_task_data_t *taskData = new ble_task_data_t{this, task, cb_type, buf}; - ble_uuid16_t uuid {{BLE_UUID_TYPE_16}, BLE_SVC_GAP_CHR_UUID16_DEVICE_NAME}; - int rc = ble_gattc_read_by_uuid(conn_handle, - 1, - 0xffff, - ((ble_uuid_t*)&uuid), - NimBLEServer::peerNameCB, - taskData); - if (rc != 0) { - NIMBLE_LOGE(LOG_TAG, "ble_gattc_read_by_uuid rc=%d, %s", rc, NimBLEUtils::returnCodeToString(rc)); - NimBLEConnInfo peerInfo{}; - ble_gap_conn_find(conn_handle, &peerInfo.m_desc); - if (cb_type == NIMBLE_SERVER_GET_PEER_NAME_ON_CONNECT_CB) { - m_pServerCallbacks->onConnect(this, peerInfo, *buf); - } else if (cb_type == NIMBLE_SERVER_GET_PEER_NAME_ON_AUTH_CB) { - m_pServerCallbacks->onAuthenticationComplete(peerInfo, *buf); - } - delete buf; - delete taskData; - } else if (task != nullptr) { -#ifdef ulTaskNotifyValueClear - // Clear the task notification value to ensure we block - ulTaskNotifyValueClear(task, ULONG_MAX); -#endif - ulTaskNotifyTake(pdTRUE, portMAX_DELAY); - rc = taskData->rc; - std::string name{*(std::string*)taskData->buf}; - delete buf; - delete taskData; - - if (rc != 0 && rc != BLE_HS_EDONE) { - NIMBLE_LOGE(LOG_TAG, "getPeerName rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc)); - } - - return name; - } - // TaskData and name buffer will be deleted in the callback. - return ""; -} - -/** - * @brief Get the name of the connected peer. - * @param connInfo A reference to a NimBLEConnInfo instance to read the name from. - * @returns A string containing the name. - * @note This is a blocking call and should NOT be called from any callbacks! - */ -std::string NimBLEServer::getPeerName(const NimBLEConnInfo& connInfo) { - std::string name = getPeerNameInternal(connInfo.getConnHandle(), xTaskGetCurrentTaskHandle()); - return name; -} - -/** - * @brief Handle a GATT Server Event. - * - * @param [in] event - * @param [in] gatts_if - * @param [in] param - * - */ -/*STATIC*/ -int NimBLEServer::handleGapEvent(struct ble_gap_event *event, void *arg) { +int NimBLEServer::handleGapEvent(ble_gap_event* event, void* arg) { NIMBLE_LOGD(LOG_TAG, ">> handleGapEvent: %s", NimBLEUtils::gapEventToString(event->type)); - int rc = 0; - NimBLEConnInfo peerInfo; - NimBLEServer* pServer = NimBLEDevice::getServer(); - - switch(event->type) { + int rc = 0; + NimBLEConnInfo peerInfo{}; + NimBLEServer* pServer = NimBLEDevice::getServer(); + switch (event->type) { case BLE_GAP_EVENT_CONNECT: { if (event->connect.status != 0) { - /* Connection failed; resume advertising */ NIMBLE_LOGE(LOG_TAG, "Connection failed"); -#if !CONFIG_BT_NIMBLE_EXT_ADV +# if !CONFIG_BT_NIMBLE_EXT_ADV && CONFIG_BT_NIMBLE_ROLE_BROADCASTER NimBLEDevice::startAdvertising(); -#endif +# endif } else { rc = ble_gap_conn_find(event->connect.conn_handle, &peerInfo.m_desc); if (rc != 0) { return 0; } - pServer->m_connectedPeersVec.push_back(event->connect.conn_handle); - - if (pServer->m_getPeerNameOnConnect) { - pServer->getPeerNameInternal(event->connect.conn_handle, - nullptr, - NIMBLE_SERVER_GET_PEER_NAME_ON_CONNECT_CB); - } else { - pServer->m_pServerCallbacks->onConnect(pServer, peerInfo); + for (auto& peer : pServer->m_connectedPeers) { + if (peer == BLE_HS_CONN_HANDLE_NONE) { + peer = event->connect.conn_handle; + break; + } } + pServer->m_pServerCallbacks->onConnect(pServer, peerInfo); } - return 0; + break; } // BLE_GAP_EVENT_CONNECT - case BLE_GAP_EVENT_DISCONNECT: { // If Host reset tell the device now before returning to prevent // any errors caused by calling host functions before resync. - switch(event->disconnect.reason) { + switch (event->disconnect.reason) { case BLE_HS_ETIMEOUT_HCI: case BLE_HS_EOS: case BLE_HS_ECONTROLLER: @@ -523,107 +393,114 @@ int NimBLEServer::handleGapEvent(struct ble_gap_event *event, void *arg) { break; } - pServer->m_connectedPeersVec.erase(std::remove(pServer->m_connectedPeersVec.begin(), - pServer->m_connectedPeersVec.end(), - event->disconnect.conn.conn_handle), - pServer->m_connectedPeersVec.end()); + for (auto& peer : pServer->m_connectedPeers) { + if (peer == event->disconnect.conn.conn_handle) { + peer = BLE_HS_CONN_HANDLE_NONE; + break; + } + } - if(pServer->m_svcChanged) { +# if CONFIG_BT_NIMBLE_ROLE_CENTRAL + if (pServer->m_pClient && pServer->m_pClient->m_connHandle == event->disconnect.conn.conn_handle) { + // If this was also the client make sure it's flagged as disconnected. + pServer->m_pClient->m_connHandle = BLE_HS_CONN_HANDLE_NONE; + } +# endif + + if (pServer->m_svcChanged) { pServer->resetGATT(); } - NimBLEConnInfo peerInfo(event->disconnect.conn); + peerInfo.m_desc = event->disconnect.conn; pServer->m_pServerCallbacks->onDisconnect(pServer, peerInfo, event->disconnect.reason); - -#if !CONFIG_BT_NIMBLE_EXT_ADV - if(pServer->m_advertiseOnDisconnect) { +# if !CONFIG_BT_NIMBLE_EXT_ADV + if (pServer->m_advertiseOnDisconnect) { pServer->startAdvertising(); } -#endif - return 0; +# endif + break; } // BLE_GAP_EVENT_DISCONNECT case BLE_GAP_EVENT_SUBSCRIBE: { - NIMBLE_LOGI(LOG_TAG, "subscribe event; attr_handle=%d, subscribed: %s", - event->subscribe.attr_handle, - (event->subscribe.cur_notify ? "true":"false")); + NIMBLE_LOGI(LOG_TAG, + "subscribe event; attr_handle=%d, subscribed: %s", + event->subscribe.attr_handle, + ((event->subscribe.cur_notify || event->subscribe.cur_indicate) ? "true" : "false")); - for(auto &it : pServer->m_notifyChrVec) { - if(it->getHandle() == event->subscribe.attr_handle) { - if((it->getProperties() & BLE_GATT_CHR_F_READ_AUTHEN) || - (it->getProperties() & BLE_GATT_CHR_F_READ_AUTHOR) || - (it->getProperties() & BLE_GATT_CHR_F_READ_ENC)) - { + for (const auto& svc : pServer->m_svcVec) { + for (const auto& chr : svc->m_vChars) { + if (chr->getHandle() == event->subscribe.attr_handle) { rc = ble_gap_conn_find(event->subscribe.conn_handle, &peerInfo.m_desc); if (rc != 0) { break; } - if(!peerInfo.isEncrypted()) { + auto chrProps = chr->getProperties(); + if (!peerInfo.isEncrypted() && + (chrProps & BLE_GATT_CHR_F_READ_AUTHEN || chrProps & BLE_GATT_CHR_F_READ_AUTHOR || + chrProps & BLE_GATT_CHR_F_READ_ENC)) { NimBLEDevice::startSecurity(event->subscribe.conn_handle); } - } - it->setSubscribe(event); - break; + chr->m_pCallbacks->onSubscribe(chr, + peerInfo, + event->subscribe.cur_notify + (event->subscribe.cur_indicate << 1)); + } } } - return 0; + break; } // BLE_GAP_EVENT_SUBSCRIBE case BLE_GAP_EVENT_MTU: { - NIMBLE_LOGI(LOG_TAG, "mtu update event; conn_handle=%d mtu=%d", - event->mtu.conn_handle, - event->mtu.value); - - rc = ble_gap_conn_find(event->mtu.conn_handle, &peerInfo.m_desc); - if (rc != 0) { - return 0; + NIMBLE_LOGI(LOG_TAG, "mtu update event; conn_handle=%d mtu=%d", event->mtu.conn_handle, event->mtu.value); + if (ble_gap_conn_find(event->mtu.conn_handle, &peerInfo.m_desc) == 0) { + pServer->m_pServerCallbacks->onMTUChange(event->mtu.value, peerInfo); } - pServer->m_pServerCallbacks->onMTUChange(event->mtu.value, peerInfo); - return 0; + break; } // BLE_GAP_EVENT_MTU case BLE_GAP_EVENT_NOTIFY_TX: { - NimBLECharacteristic *pChar = nullptr; + NimBLECharacteristic* pChar = nullptr; - for(auto &it : pServer->m_notifyChrVec) { - if(it->getHandle() == event->notify_tx.attr_handle) { - pChar = it; + for (const auto& svc : pServer->m_svcVec) { + for (auto& chr : svc->m_vChars) { + if (chr->getHandle() == event->notify_tx.attr_handle) { + pChar = chr; + } } } - if(pChar == nullptr) { + if (pChar == nullptr) { return 0; } - if(event->notify_tx.indication) { - if(event->notify_tx.status == 0) { + if (event->notify_tx.indication) { + if (event->notify_tx.status == 0) { return 0; // Indication sent but not yet acknowledged. } - pServer->clearIndicateWait(event->notify_tx.conn_handle); } pChar->m_pCallbacks->onStatus(pChar, event->notify_tx.status); - - return 0; + break; } // BLE_GAP_EVENT_NOTIFY_TX - - case BLE_GAP_EVENT_ADV_COMPLETE: -#if CONFIG_BT_NIMBLE_EXT_ADV - case BLE_GAP_EVENT_SCAN_REQ_RCVD: - return NimBLEExtAdvertising::handleGapEvent(event, arg); -#else + case BLE_GAP_EVENT_ADV_COMPLETE: { +# if CONFIG_BT_NIMBLE_EXT_ADV && CONFIG_BT_NIMBLE_ROLE_BROADCASTER + case BLE_GAP_EVENT_SCAN_REQ_RCVD: + return NimBLEExtAdvertising::handleGapEvent(event, arg); +# elif CONFIG_BT_NIMBLE_ROLE_BROADCASTER return NimBLEAdvertising::handleGapEvent(event, arg); -#endif - // BLE_GAP_EVENT_ADV_COMPLETE | BLE_GAP_EVENT_SCAN_REQ_RCVD +# endif + } // BLE_GAP_EVENT_ADV_COMPLETE | BLE_GAP_EVENT_SCAN_REQ_RCVD case BLE_GAP_EVENT_CONN_UPDATE: { - NIMBLE_LOGD(LOG_TAG, "Connection parameters updated."); - return 0; + if (ble_gap_conn_find(event->connect.conn_handle, &peerInfo.m_desc) == 0) { + pServer->m_pServerCallbacks->onConnParamsUpdate(peerInfo); + } + + break; } // BLE_GAP_EVENT_CONN_UPDATE case BLE_GAP_EVENT_REPEAT_PAIRING: { @@ -634,7 +511,7 @@ int NimBLEServer::handleGapEvent(struct ble_gap_event *event, void *arg) { /* Delete the old bond. */ rc = ble_gap_conn_find(event->repeat_pairing.conn_handle, &peerInfo.m_desc); - if (rc != 0){ + if (rc != 0) { return BLE_GAP_REPEAT_PAIRING_IGNORE; } @@ -648,40 +525,49 @@ int NimBLEServer::handleGapEvent(struct ble_gap_event *event, void *arg) { case BLE_GAP_EVENT_ENC_CHANGE: { rc = ble_gap_conn_find(event->enc_change.conn_handle, &peerInfo.m_desc); - if(rc != 0) { + if (rc != 0) { return BLE_ATT_ERR_INVALID_HANDLE; } - if (pServer->m_getPeerNameOnConnect) { - pServer->getPeerNameInternal(event->enc_change.conn_handle, - nullptr, - NIMBLE_SERVER_GET_PEER_NAME_ON_AUTH_CB); - } else { - pServer->m_pServerCallbacks->onAuthenticationComplete(peerInfo); + pServer->m_pServerCallbacks->onAuthenticationComplete(peerInfo); +# if CONFIG_BT_NIMBLE_ROLE_CENTRAL + if (pServer->m_pClient && pServer->m_pClient->m_connHandle == event->enc_change.conn_handle) { + NimBLEClient::handleGapEvent(event, pServer->m_pClient); } - return 0; +# endif + break; } // BLE_GAP_EVENT_ENC_CHANGE case BLE_GAP_EVENT_IDENTITY_RESOLVED: { rc = ble_gap_conn_find(event->identity_resolved.conn_handle, &peerInfo.m_desc); - if(rc != 0) { + if (rc != 0) { return BLE_ATT_ERR_INVALID_HANDLE; } pServer->m_pServerCallbacks->onIdentity(peerInfo); - return 0; + break; } // BLE_GAP_EVENT_IDENTITY_RESOLVED + case BLE_GAP_EVENT_PHY_UPDATE_COMPLETE: { + rc = ble_gap_conn_find(event->phy_updated.conn_handle, &peerInfo.m_desc); + if (rc != 0) { + return BLE_ATT_ERR_INVALID_HANDLE; + } + + pServer->m_pServerCallbacks->onPhyUpdate(peerInfo, event->phy_updated.tx_phy, event->phy_updated.rx_phy); + return 0; + } // BLE_GAP_EVENT_PHY_UPDATE_COMPLETE + case BLE_GAP_EVENT_PASSKEY_ACTION: { - struct ble_sm_io pkey = {0,0}; + struct ble_sm_io pkey = {0, 0}; if (event->passkey.params.action == BLE_SM_IOACT_DISP) { - pkey.action = event->passkey.params.action; + pkey.action = event->passkey.params.action; // backward compatibility pkey.passkey = NimBLEDevice::getSecurityPasskey(); // This is the passkey to be entered on peer // if the (static)passkey is the default, check the callback for custom value // both values default to the same. - if(pkey.passkey == 123456) { + if (pkey.passkey == 123456) { pkey.passkey = pServer->m_pServerCallbacks->onPassKeyDisplay(); } rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey); @@ -691,58 +577,116 @@ int NimBLEServer::handleGapEvent(struct ble_gap_event *event, void *arg) { NIMBLE_LOGD(LOG_TAG, "Passkey on device's display: %" PRIu32, event->passkey.params.numcmp); rc = ble_gap_conn_find(event->passkey.conn_handle, &peerInfo.m_desc); - if(rc != 0) { + if (rc != 0) { return BLE_ATT_ERR_INVALID_HANDLE; } - pServer->m_pServerCallbacks->onConfirmPIN(peerInfo, event->passkey.params.numcmp); - //TODO: Handle out of band pairing + pServer->m_pServerCallbacks->onConfirmPassKey(peerInfo, event->passkey.params.numcmp); } else if (event->passkey.params.action == BLE_SM_IOACT_OOB) { - static uint8_t tem_oob[16] = {0}; - pkey.action = event->passkey.params.action; - for (int i = 0; i < 16; i++) { - pkey.oob[i] = tem_oob[i]; - } - rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey); - NIMBLE_LOGD(LOG_TAG, "BLE_SM_IOACT_OOB; ble_sm_inject_io result: %d", rc); - ////////////////////////////////// + // TODO: Handle out of band pairing + // static uint8_t tem_oob[16] = {0}; + // pkey.action = event->passkey.params.action; + // for (int i = 0; i < 16; i++) { + // pkey.oob[i] = tem_oob[i]; + // } + // rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey); + // NIMBLE_LOGD(LOG_TAG, "BLE_SM_IOACT_OOB; ble_sm_inject_io result: %d", rc); } else if (event->passkey.params.action == BLE_SM_IOACT_NONE) { NIMBLE_LOGD(LOG_TAG, "No passkey action required"); } - NIMBLE_LOGD(LOG_TAG, "<< handleGATTServerEvent"); - return 0; + break; } // BLE_GAP_EVENT_PASSKEY_ACTION default: break; } - NIMBLE_LOGD(LOG_TAG, "<< handleGATTServerEvent"); + NIMBLE_LOGD(LOG_TAG, "<< handleGapEvent"); return 0; } // handleGapEvent +/** + * @brief GATT event handler. + */ +int NimBLEServer::handleGattEvent(uint16_t connHandle, uint16_t attrHandle, ble_gatt_access_ctxt* ctxt, void* arg) { + NIMBLE_LOGD(LOG_TAG, + "Gatt %s event", + (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR || ctxt->op == BLE_GATT_ACCESS_OP_READ_DSC) ? "Read" : "Write"); + auto pAtt = static_cast(arg); + const NimBLEAttValue& val = pAtt->getAttVal(); + + NimBLEConnInfo peerInfo{}; + ble_gap_conn_find(connHandle, &peerInfo.m_desc); + + switch (ctxt->op) { + case BLE_GATT_ACCESS_OP_READ_DSC: + case BLE_GATT_ACCESS_OP_READ_CHR: { + // Don't call readEvent if the buffer len is 0 (this is a follow up to a previous read), + // or if this is an internal read (handle is NONE) + if (ctxt->om->om_len > 0 && connHandle != BLE_HS_CONN_HANDLE_NONE) { + pAtt->readEvent(peerInfo); + } + + ble_npl_hw_enter_critical(); + int rc = os_mbuf_append(ctxt->om, val.data(), val.size()); + ble_npl_hw_exit_critical(0); + return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; + } + + case BLE_GATT_ACCESS_OP_WRITE_DSC: + case BLE_GATT_ACCESS_OP_WRITE_CHR: { + uint16_t maxLen = val.max_size(); + uint16_t len = ctxt->om->om_len; + if (len > maxLen) { + return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } + + uint8_t buf[maxLen]; + memcpy(buf, ctxt->om->om_data, len); + + os_mbuf* next; + next = SLIST_NEXT(ctxt->om, om_next); + while (next != NULL) { + if ((len + next->om_len) > maxLen) { + return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } + memcpy(&buf[len], next->om_data, next->om_len); + len += next->om_len; + next = SLIST_NEXT(next, om_next); + } + + pAtt->writeEvent(buf, len, peerInfo); + return 0; + } + + default: + break; + } + + return BLE_ATT_ERR_UNLIKELY; +} // handleGattEvent /** * @brief Set the server callbacks. * - * As a %BLE server operates, it will generate server level events such as a new client connecting or a previous client - * disconnecting. This function can be called to register a callback handler that will be invoked when these + * As a BLE server operates, it will generate server level events such as a new client connecting or a previous + * client disconnecting. This function can be called to register a callback handler that will be invoked when these * events are detected. * * @param [in] pCallbacks The callbacks to be invoked. * @param [in] deleteCallbacks if true callback class will be deleted when server is destructed. */ void NimBLEServer::setCallbacks(NimBLEServerCallbacks* pCallbacks, bool deleteCallbacks) { - if (pCallbacks != nullptr){ + if (pCallbacks != nullptr) { m_pServerCallbacks = pCallbacks; - m_deleteCallbacks = deleteCallbacks; + m_deleteCallbacks = deleteCallbacks; } else { m_pServerCallbacks = &defaultCallbacks; + m_deleteCallbacks = false; } } // setCallbacks - /** * @brief Remove a service from the server. * @@ -765,9 +709,9 @@ void NimBLEServer::removeService(NimBLEService* service, bool deleteSvc) { // Check if the service was already removed and if so check if this // is being called to delete the object and do so if requested. // Otherwise, ignore the call and return. - if(service->m_removed > 0) { - if(deleteSvc) { - for(auto it = m_svcVec.begin(); it != m_svcVec.end(); ++it) { + if (service->getRemoved() > 0) { + if (deleteSvc) { + for (auto it = m_svcVec.begin(); it != m_svcVec.end(); ++it) { if ((*it) == service) { delete *it; m_svcVec.erase(it); @@ -780,17 +724,16 @@ void NimBLEServer::removeService(NimBLEService* service, bool deleteSvc) { } int rc = ble_gatts_svc_set_visibility(service->getHandle(), 0); - if(rc !=0) { + if (rc != 0) { return; } - service->m_removed = deleteSvc ? NIMBLE_ATT_REMOVE_DELETE : NIMBLE_ATT_REMOVE_HIDE; + service->setRemoved(deleteSvc ? NIMBLE_ATT_REMOVE_DELETE : NIMBLE_ATT_REMOVE_HIDE); serviceChanged(); -#if !CONFIG_BT_NIMBLE_EXT_ADV +# if !CONFIG_BT_NIMBLE_EXT_ADV && CONFIG_BT_NIMBLE_ROLE_BROADCASTER NimBLEDevice::getAdvertising()->removeServiceUUID(service->getUUID()); -#endif -} - +# endif +} // removeService /** * @brief Adds a service which was either already created but removed from availability,\n @@ -801,39 +744,39 @@ void NimBLEServer::removeService(NimBLEService* service, bool deleteSvc) { */ void NimBLEServer::addService(NimBLEService* service) { // Check that a service with the supplied UUID does not already exist. - if(getServiceByUUID(service->getUUID()) != nullptr) { - NIMBLE_LOGW(LOG_TAG, "Warning creating a duplicate service UUID: %s", - std::string(service->getUUID()).c_str()); + if (getServiceByUUID(service->getUUID()) != nullptr) { + NIMBLE_LOGW(LOG_TAG, "Warning creating a duplicate service UUID: %s", std::string(service->getUUID()).c_str()); } // If adding a service that was not removed add it and return. // Else reset GATT and send service changed notification. - if(service->m_removed == 0) { + if (service->getRemoved() == 0) { m_svcVec.push_back(service); return; } - service->m_removed = 0; + service->setRemoved(0); serviceChanged(); -} - +} // addService /** * @brief Resets the GATT server, used when services are added/removed after initialization. */ void NimBLEServer::resetGATT() { - if(getConnectedCount() > 0) { + if (getConnectedCount() > 0) { return; } +# if CONFIG_BT_NIMBLE_ROLE_BROADCASTER NimBLEDevice::stopAdvertising(); +# endif ble_gatts_reset(); ble_svc_gap_init(); ble_svc_gatt_init(); - for(auto it = m_svcVec.begin(); it != m_svcVec.end(); ) { - if ((*it)->m_removed > 0) { - if ((*it)->m_removed == NIMBLE_ATT_REMOVE_DELETE) { + for (auto it = m_svcVec.begin(); it != m_svcVec.end();) { + if ((*it)->getRemoved() > 0) { + if ((*it)->getRemoved() == NIMBLE_ATT_REMOVE_DELETE) { delete *it; it = m_svcVec.erase(it); } else { @@ -846,39 +789,79 @@ void NimBLEServer::resetGATT() { ++it; } - m_svcChanged = false; m_gattsStarted = false; -} +} // resetGATT +/** + * @brief Request an update to the PHY used for a peer connection. + * @param [in] connHandle the connection handle to the update the PHY for. + * @param [in] txPhyMask TX PHY. Can be mask of following: + * - BLE_GAP_LE_PHY_1M_MASK + * - BLE_GAP_LE_PHY_2M_MASK + * - BLE_GAP_LE_PHY_CODED_MASK + * - BLE_GAP_LE_PHY_ANY_MASK + * @param [in] rxPhyMask RX PHY. Can be mask of following: + * - BLE_GAP_LE_PHY_1M_MASK + * - BLE_GAP_LE_PHY_2M_MASK + * - BLE_GAP_LE_PHY_CODED_MASK + * - BLE_GAP_LE_PHY_ANY_MASK + * @param phyOptions Additional PHY options. Valid values are: + * - BLE_GAP_LE_PHY_CODED_ANY (default) + * - BLE_GAP_LE_PHY_CODED_S2 + * - BLE_GAP_LE_PHY_CODED_S8 + * @return True if successful. + */ +bool NimBLEServer::updatePhy(uint16_t connHandle, uint8_t txPhyMask, uint8_t rxPhyMask, uint16_t phyOptions) { + int rc = ble_gap_set_prefered_le_phy(connHandle, txPhyMask, rxPhyMask, phyOptions); + if (rc != 0) { + NIMBLE_LOGE(LOG_TAG, "Failed to update phy; rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc)); + } -#if CONFIG_BT_NIMBLE_EXT_ADV + return rc == 0; +} // updatePhy + +/** + * @brief Get the PHY used for a peer connection. + * @param [in] connHandle the connection handle to the get the PHY for. + * @param [out] txPhy The TX PHY. + * @param [out] rxPhy The RX PHY. + * @return True if successful. + */ +bool NimBLEServer::getPhy(uint16_t connHandle, uint8_t* txPhy, uint8_t* rxPhy) { + int rc = ble_gap_read_le_phy(connHandle, txPhy, rxPhy); + if (rc != 0) { + NIMBLE_LOGE(LOG_TAG, "Failed to read phy; rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc)); + } + + return rc == 0; +} // getPhy + +# if CONFIG_BT_NIMBLE_EXT_ADV /** * @brief Start advertising. - * @param [in] inst_id The extended advertisement instance ID to start. + * @param [in] instId The extended advertisement instance ID to start. * @param [in] duration How long to advertise for in milliseconds, 0 = forever (default). - * @param [in] max_events Maximum number of advertisement events to send, 0 = no limit (default). + * @param [in] maxEvents Maximum number of advertisement events to send, 0 = no limit (default). * @return True if advertising started successfully. * @details Start the server advertising its existence. This is a convenience function and is equivalent to * retrieving the advertising object and invoking start upon it. */ -bool NimBLEServer::startAdvertising(uint8_t inst_id, - int duration, - int max_events) { - return getAdvertising()->start(inst_id, duration, max_events); +bool NimBLEServer::startAdvertising(uint8_t instId, int duration, int maxEvents) const { + return getAdvertising()->start(instId, duration, maxEvents); } // startAdvertising - /** * @brief Convenience function to stop advertising a data set. - * @param [in] inst_id The extended advertisement instance ID to stop advertising. + * @param [in] instId The extended advertisement instance ID to stop advertising. * @return True if advertising stopped successfully. */ -bool NimBLEServer::stopAdvertising(uint8_t inst_id) { - return getAdvertising()->stop(inst_id); +bool NimBLEServer::stopAdvertising(uint8_t instId) const { + return getAdvertising()->stop(instId); } // stopAdvertising -#endif -#if !CONFIG_BT_NIMBLE_EXT_ADV || defined(_DOXYGEN_) +# endif + +# if (!CONFIG_BT_NIMBLE_EXT_ADV && CONFIG_BT_NIMBLE_ROLE_BROADCASTER) || defined(_DOXYGEN_) /** * @brief Start advertising. * @param [in] duration The duration in milliseconds to advertise for, default = forever. @@ -886,115 +869,129 @@ bool NimBLEServer::stopAdvertising(uint8_t inst_id) { * @details Start the server advertising its existence. This is a convenience function and is equivalent to * retrieving the advertising object and invoking start upon it. */ -bool NimBLEServer::startAdvertising(uint32_t duration) { +bool NimBLEServer::startAdvertising(uint32_t duration) const { return getAdvertising()->start(duration); } // startAdvertising -#endif - /** * @brief Stop advertising. * @return True if advertising stopped successfully. */ -bool NimBLEServer::stopAdvertising() { +bool NimBLEServer::stopAdvertising() const { return getAdvertising()->stop(); } // stopAdvertising - +# endif /** - * @brief Get the MTU of the client. - * @returns The client MTU or 0 if not found/connected. + * @brief Get the MTU value of a client connection. + * @param [in] connHandle The connection handle of the client to get the MTU value for. + * @returns The MTU or 0 if not found/connected. */ -uint16_t NimBLEServer::getPeerMTU(uint16_t conn_id) { - return ble_att_mtu(conn_id); -} //getPeerMTU - +uint16_t NimBLEServer::getPeerMTU(uint16_t connHandle) const { + return ble_att_mtu(connHandle); +} // getPeerMTU /** * @brief Request an Update the connection parameters: * * Can only be used after a connection has been established. - * @param [in] conn_handle The connection handle of the peer to send the request to. + * @param [in] connHandle The connection handle of the peer to send the request to. * @param [in] minInterval The minimum connection interval in 1.25ms units. * @param [in] maxInterval The maximum connection interval in 1.25ms units. * @param [in] latency The number of packets allowed to skip (extends max interval). * @param [in] timeout The timeout time in 10ms units before disconnecting. */ -void NimBLEServer::updateConnParams(uint16_t conn_handle, - uint16_t minInterval, uint16_t maxInterval, - uint16_t latency, uint16_t timeout) -{ - ble_gap_upd_params params; +void NimBLEServer::updateConnParams( + uint16_t connHandle, uint16_t minInterval, uint16_t maxInterval, uint16_t latency, uint16_t timeout) const { + ble_gap_upd_params params = {.itvl_min = minInterval, + .itvl_max = maxInterval, + .latency = latency, + .supervision_timeout = timeout, + .min_ce_len = BLE_GAP_INITIAL_CONN_MIN_CE_LEN, + .max_ce_len = BLE_GAP_INITIAL_CONN_MAX_CE_LEN}; - params.latency = latency; - params.itvl_max = maxInterval; // max_int = 0x20*1.25ms = 40ms - params.itvl_min = minInterval; // min_int = 0x10*1.25ms = 20ms - params.supervision_timeout = timeout; // timeout = 400*10ms = 4000ms - params.min_ce_len = BLE_GAP_INITIAL_CONN_MIN_CE_LEN; // Minimum length of connection event in 0.625ms units - params.max_ce_len = BLE_GAP_INITIAL_CONN_MAX_CE_LEN; // Maximum length of connection event in 0.625ms units - - int rc = ble_gap_update_params(conn_handle, ¶ms); - if(rc != 0) { + int rc = ble_gap_update_params(connHandle, ¶ms); + if (rc != 0) { NIMBLE_LOGE(LOG_TAG, "Update params error: %d, %s", rc, NimBLEUtils::returnCodeToString(rc)); } } // updateConnParams - /** * @brief Request an update of the data packet length. * * Can only be used after a connection has been established. * @details Sends a data length update request to the peer. * The Data Length Extension (DLE) allows to increase the Data Channel Payload from 27 bytes to up to 251 bytes. * The peer needs to support the Bluetooth 4.2 specifications, to be capable of DLE. - * @param [in] conn_handle The connection handle of the peer to send the request to. - * @param [in] tx_octets The preferred number of payload octets to use (Range 0x001B-0x00FB). + * @param [in] connHandle The connection handle of the peer to send the request to. + * @param [in] octets The preferred number of payload octets to use (Range 0x001B-0x00FB). */ -void NimBLEServer::setDataLen(uint16_t conn_handle, uint16_t tx_octets) { -#if defined(CONFIG_NIMBLE_CPP_IDF) && !defined(ESP_IDF_VERSION) || \ - (ESP_IDF_VERSION_MAJOR * 100 + ESP_IDF_VERSION_MINOR * 10 + ESP_IDF_VERSION_PATCH) < 432 +void NimBLEServer::setDataLen(uint16_t connHandle, uint16_t octets) const { +# if defined(CONFIG_NIMBLE_CPP_IDF) && !defined(ESP_IDF_VERSION) || \ + (ESP_IDF_VERSION_MAJOR * 100 + ESP_IDF_VERSION_MINOR * 10 + ESP_IDF_VERSION_PATCH) < 432 return; -#else - uint16_t tx_time = (tx_octets + 14) * 8; +# else + uint16_t tx_time = (octets + 14) * 8; - int rc = ble_gap_set_data_len(conn_handle, tx_octets, tx_time); - if(rc != 0) { + int rc = ble_gap_set_data_len(connHandle, octets, tx_time); + if (rc != 0) { NIMBLE_LOGE(LOG_TAG, "Set data length error: %d, %s", rc, NimBLEUtils::returnCodeToString(rc)); } -#endif +# endif } // setDataLen - -bool NimBLEServer::setIndicateWait(uint16_t conn_handle) { - for(auto i = 0; i < CONFIG_BT_NIMBLE_MAX_CONNECTIONS; i++) { - if(m_indWait[i] == conn_handle) { - return false; - } +# if CONFIG_BT_NIMBLE_ROLE_CENTRAL +/** + * @brief Create a client instance from the connection handle. + * @param [in] connHandle The connection handle to create a client instance from. + * @return A pointer to the NimBLEClient instance or nullptr if there was an error. + * @note Only one instance is supported subsequent calls will overwrite the previous + * client connection information and data. + */ +NimBLEClient* NimBLEServer::getClient(uint16_t connHandle) { + NimBLEConnInfo connInfo; + int rc = ble_gap_conn_find(connHandle, &connInfo.m_desc); + if (rc != 0) { + NIMBLE_LOGE(LOG_TAG, "Client info not found"); + return nullptr; } - return true; -} + return getClient(connInfo); +} // getClient - -void NimBLEServer::clearIndicateWait(uint16_t conn_handle) { - for(auto i = 0; i < CONFIG_BT_NIMBLE_MAX_CONNECTIONS; i++) { - if(m_indWait[i] == conn_handle) { - m_indWait[i] = BLE_HS_CONN_HANDLE_NONE; - return; - } +/** + * @brief Create a client instance from the NimBLEConnInfo reference. + * @param [in] connInfo The connection info to create a client instance from. + * @return A pointer to the NimBLEClient instance or nullptr if there was an error. + * @note Only one instance is supported subsequent calls will overwrite the previous + * client connection information and data. + */ +NimBLEClient* NimBLEServer::getClient(const NimBLEConnInfo& connInfo) { + if (m_pClient == nullptr) { + m_pClient = new NimBLEClient(connInfo.getAddress()); } -} + m_pClient->deleteServices(); // Changed peer connection delete the database. + m_pClient->m_peerAddress = connInfo.getAddress(); + m_pClient->m_connHandle = connInfo.getConnHandle(); + return m_pClient; +} // getClient + +/** + * @brief Delete the NimBLEClient instance that was created with `getClient()` + */ +void NimBLEServer::deleteClient() { + if (m_pClient != nullptr) { + delete m_pClient; + m_pClient = nullptr; + } +} // deleteClient +# endif /** Default callback handlers */ void NimBLEServerCallbacks::onConnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo) { NIMBLE_LOGD("NimBLEServerCallbacks", "onConnect(): Default"); } // onConnect -void NimBLEServerCallbacks::onConnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo, std::string& name) { - NIMBLE_LOGD("NimBLEServerCallbacks", "onConnect(): Default"); -} // onConnect - -void NimBLEServerCallbacks::onDisconnect(NimBLEServer* pServer, - NimBLEConnInfo& connInfo, int reason) { +void NimBLEServerCallbacks::onDisconnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo, int reason) { NIMBLE_LOGD("NimBLEServerCallbacks", "onDisconnect(): Default"); } // onDisconnect @@ -1002,26 +999,30 @@ void NimBLEServerCallbacks::onMTUChange(uint16_t MTU, NimBLEConnInfo& connInfo) NIMBLE_LOGD("NimBLEServerCallbacks", "onMTUChange(): Default"); } // onMTUChange -uint32_t NimBLEServerCallbacks::onPassKeyDisplay(){ +uint32_t NimBLEServerCallbacks::onPassKeyDisplay() { NIMBLE_LOGD("NimBLEServerCallbacks", "onPassKeyDisplay: default: 123456"); return 123456; -} //onPassKeyDisplay +} // onPassKeyDisplay -void NimBLEServerCallbacks::onConfirmPIN(const NimBLEConnInfo& connInfo, uint32_t pin){ - NIMBLE_LOGD("NimBLEServerCallbacks", "onConfirmPIN: default: true"); - NimBLEDevice::injectConfirmPIN(connInfo, true); -} // onConfirmPIN +void NimBLEServerCallbacks::onConfirmPassKey(NimBLEConnInfo& connInfo, uint32_t pin) { + NIMBLE_LOGD("NimBLEServerCallbacks", "onConfirmPasskey: default: true"); + NimBLEDevice::injectConfirmPasskey(connInfo, true); +} // onConfirmPasskey -void NimBLEServerCallbacks::onIdentity(const NimBLEConnInfo& connInfo){ +void NimBLEServerCallbacks::onIdentity(NimBLEConnInfo& connInfo) { NIMBLE_LOGD("NimBLEServerCallbacks", "onIdentity: default"); } // onIdentity -void NimBLEServerCallbacks::onAuthenticationComplete(const NimBLEConnInfo& connInfo){ +void NimBLEServerCallbacks::onAuthenticationComplete(NimBLEConnInfo& connInfo) { NIMBLE_LOGD("NimBLEServerCallbacks", "onAuthenticationComplete: default"); } // onAuthenticationComplete -void NimBLEServerCallbacks::onAuthenticationComplete(const NimBLEConnInfo& connInfo, const std::string& name){ - NIMBLE_LOGD("NimBLEServerCallbacks", "onAuthenticationComplete: default"); -} // onAuthenticationComplete +void NimBLEServerCallbacks::onConnParamsUpdate(NimBLEConnInfo& connInfo) { + NIMBLE_LOGD("NimBLEServerCallbacks", "onConnParamsUpdate: default"); +} // onConnParamsUpdate -#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL */ +void NimBLEServerCallbacks::onPhyUpdate(NimBLEConnInfo& connInfo, uint8_t txPhy, uint8_t rxPhy) { + NIMBLE_LOGD("NimBLEServerCallbacks", "onPhyUpdate: default, txPhy: %d, rxPhy: %d", txPhy, rxPhy); +} // onPhyUpdate + +#endif // CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL diff --git a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEServer.h b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEServer.h index bbb4ebfb9..f8b6423be 100644 --- a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEServer.h +++ b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEServer.h @@ -1,161 +1,165 @@ /* - * NimBLEServer.h + * Copyright 2020-2025 Ryan Powell and + * esp-nimble-cpp, NimBLE-Arduino contributors. * - * Created: on March 2, 2020 - * Author H2zero + * 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 * - * Originally: + * http://www.apache.org/licenses/LICENSE-2.0 * - * BLEServer.h - * - * Created on: Apr 16, 2017 - * Author: kolban + * 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 MAIN_NIMBLESERVER_H_ -#define MAIN_NIMBLESERVER_H_ +#ifndef NIMBLE_CPP_SERVER_H_ +#define NIMBLE_CPP_SERVER_H_ #include "nimconfig.h" -#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) +#if CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL -#define NIMBLE_ATT_REMOVE_HIDE 1 -#define NIMBLE_ATT_REMOVE_DELETE 2 +# if defined(CONFIG_NIMBLE_CPP_IDF) +# include "host/ble_gap.h" +# else +# include "nimble/nimble/host/include/host/ble_gap.h" +# endif -#define onMtuChanged onMTUChange +/**** FIX COMPILATION ****/ +# undef min +# undef max +/**************************/ -#include "NimBLEUtils.h" -#include "NimBLEAddress.h" -#if CONFIG_BT_NIMBLE_EXT_ADV -#include "NimBLEExtAdvertising.h" -#else -#include "NimBLEAdvertising.h" -#endif -#include "NimBLEService.h" -#include "NimBLEConnInfo.h" +# include +# include +# define NIMBLE_ATT_REMOVE_HIDE 1 +# define NIMBLE_ATT_REMOVE_DELETE 2 class NimBLEService; -class NimBLECharacteristic; class NimBLEServerCallbacks; - +class NimBLEUUID; +class NimBLEConnInfo; +class NimBLEAddress; +class NimBLEService; +class NimBLECharacteristic; +# if CONFIG_BT_NIMBLE_ROLE_BROADCASTER +# if CONFIG_BT_NIMBLE_EXT_ADV +class NimBLEExtAdvertising; +# else +class NimBLEAdvertising; +# endif +# endif +# if CONFIG_BT_NIMBLE_ROLE_CENTRAL +class NimBLEClient; +# endif /** - * @brief The model of a %BLE server. + * @brief The model of a BLE server. */ class NimBLEServer { -public: - size_t getConnectedCount(); - NimBLEService* createService(const char* uuid); - NimBLEService* createService(const NimBLEUUID &uuid); - void removeService(NimBLEService* service, bool deleteSvc = false); - void addService(NimBLEService* service); - void setCallbacks(NimBLEServerCallbacks* pCallbacks, - bool deleteCallbacks = true); -#if CONFIG_BT_NIMBLE_EXT_ADV - NimBLEExtAdvertising* getAdvertising(); - bool startAdvertising(uint8_t inst_id, - int duration = 0, - int max_events = 0); - bool stopAdvertising(uint8_t inst_id); -#endif -# if !CONFIG_BT_NIMBLE_EXT_ADV || defined(_DOXYGEN_) - NimBLEAdvertising* getAdvertising(); - bool startAdvertising(uint32_t duration = 0); -#endif - bool stopAdvertising(); - void start(); - NimBLEService* getServiceByUUID(const char* uuid, uint16_t instanceId = 0); - NimBLEService* getServiceByUUID(const NimBLEUUID &uuid, uint16_t instanceId = 0); - NimBLEService* getServiceByHandle(uint16_t handle); - int disconnect(uint16_t connID, - uint8_t reason = BLE_ERR_REM_USER_CONN_TERM); - int disconnect(const NimBLEConnInfo &connInfo, - uint8_t reason = BLE_ERR_REM_USER_CONN_TERM); - void updateConnParams(uint16_t conn_handle, - uint16_t minInterval, uint16_t maxInterval, - uint16_t latency, uint16_t timeout); - void setDataLen(uint16_t conn_handle, uint16_t tx_octets); - uint16_t getPeerMTU(uint16_t conn_id); - std::vector getPeerDevices(); - NimBLEConnInfo getPeerInfo(size_t index); - NimBLEConnInfo getPeerInfo(const NimBLEAddress& address); - NimBLEConnInfo getPeerIDInfo(uint16_t id); - std::string getPeerName(const NimBLEConnInfo& connInfo); - void getPeerNameOnConnect(bool enable); -#if !CONFIG_BT_NIMBLE_EXT_ADV || defined(_DOXYGEN_) - void advertiseOnDisconnect(bool); -#endif + public: + void start(); + uint8_t getConnectedCount() const; + bool disconnect(uint16_t connHandle, uint8_t reason = BLE_ERR_REM_USER_CONN_TERM) const; + bool disconnect(const NimBLEConnInfo& connInfo, uint8_t reason = BLE_ERR_REM_USER_CONN_TERM) const; + void setCallbacks(NimBLEServerCallbacks* pCallbacks, bool deleteCallbacks = true); + void updateConnParams(uint16_t connHandle, uint16_t minInterval, uint16_t maxInterval, uint16_t latency, uint16_t timeout) const; + NimBLEService* createService(const char* uuid); + NimBLEService* createService(const NimBLEUUID& uuid); + NimBLEService* getServiceByUUID(const char* uuid, uint16_t instanceId = 0) const; + NimBLEService* getServiceByUUID(const NimBLEUUID& uuid, uint16_t instanceId = 0) const; + NimBLEService* getServiceByHandle(uint16_t handle) const; + void removeService(NimBLEService* service, bool deleteSvc = false); + void addService(NimBLEService* service); + uint16_t getPeerMTU(uint16_t connHandle) const; + std::vector getPeerDevices() const; + NimBLEConnInfo getPeerInfo(uint8_t index) const; + NimBLEConnInfo getPeerInfo(const NimBLEAddress& address) const; + NimBLEConnInfo getPeerInfoByHandle(uint16_t connHandle) const; + void advertiseOnDisconnect(bool enable); + void setDataLen(uint16_t connHandle, uint16_t tx_octets) const; + bool updatePhy(uint16_t connHandle, uint8_t txPhysMask, uint8_t rxPhysMask, uint16_t phyOptions); + bool getPhy(uint16_t connHandle, uint8_t* txPhy, uint8_t* rxPhy); + +# if CONFIG_BT_NIMBLE_ROLE_CENTRAL + NimBLEClient* getClient(uint16_t connHandle); + NimBLEClient* getClient(const NimBLEConnInfo& connInfo); + void deleteClient(); +# endif + +# if CONFIG_BT_NIMBLE_ROLE_BROADCASTER +# if CONFIG_BT_NIMBLE_EXT_ADV + NimBLEExtAdvertising* getAdvertising() const; + bool startAdvertising(uint8_t instanceId, int duration = 0, int maxEvents = 0) const; + bool stopAdvertising(uint8_t instanceId) const; +# endif + +# if !CONFIG_BT_NIMBLE_EXT_ADV || defined(_DOXYGEN_) + NimBLEAdvertising* getAdvertising() const; + bool startAdvertising(uint32_t duration = 0) const; + bool stopAdvertising() const; +# endif +# endif + + private: + friend class NimBLEDevice; + friend class NimBLEService; + friend class NimBLECharacteristic; +# if CONFIG_BT_NIMBLE_ROLE_BROADCASTER +# if CONFIG_BT_NIMBLE_EXT_ADV + friend class NimBLEExtAdvertising; +# else + friend class NimBLEAdvertising; +# endif +# endif -private: NimBLEServer(); ~NimBLEServer(); - friend class NimBLECharacteristic; - friend class NimBLEService; - friend class NimBLEDevice; - friend class NimBLEAdvertising; -#if CONFIG_BT_NIMBLE_EXT_ADV - friend class NimBLEExtAdvertising; - friend class NimBLEExtAdvertisementData; -#endif - bool m_gattsStarted; -#if !CONFIG_BT_NIMBLE_EXT_ADV - bool m_advertiseOnDisconnect; -#endif - bool m_getPeerNameOnConnect; - bool m_svcChanged; - NimBLEServerCallbacks* m_pServerCallbacks; - bool m_deleteCallbacks; - uint16_t m_indWait[CONFIG_BT_NIMBLE_MAX_CONNECTIONS]; - std::vector m_connectedPeersVec; + bool m_gattsStarted : 1; + bool m_svcChanged : 1; + bool m_deleteCallbacks : 1; +# if !CONFIG_BT_NIMBLE_EXT_ADV + bool m_advertiseOnDisconnect : 1; +# endif + NimBLEServerCallbacks* m_pServerCallbacks; + std::vector m_svcVec; + std::array m_connectedPeers; -// uint16_t m_svcChgChrHdl; // Future use +# if CONFIG_BT_NIMBLE_ROLE_CENTRAL + NimBLEClient* m_pClient{nullptr}; +# endif - std::vector m_svcVec; - std::vector m_notifyChrVec; - - static int handleGapEvent(struct ble_gap_event *event, void *arg); - static int peerNameCB(uint16_t conn_handle, const struct ble_gatt_error *error, - struct ble_gatt_attr *attr, void *arg); - std::string getPeerNameInternal(uint16_t conn_handle, TaskHandle_t task, int cb_type = -1); - void serviceChanged(); - void resetGATT(); - bool setIndicateWait(uint16_t conn_handle); - void clearIndicateWait(uint16_t conn_handle); + static int handleGapEvent(struct ble_gap_event* event, void* arg); + static int handleGattEvent(uint16_t connHandle, uint16_t attrHandle, ble_gatt_access_ctxt* ctxt, void* arg); + void serviceChanged(); + void resetGATT(); }; // NimBLEServer - /** - * @brief Callbacks associated with the operation of a %BLE server. + * @brief Callbacks associated with the operation of a BLE server. */ class NimBLEServerCallbacks { -public: + public: virtual ~NimBLEServerCallbacks() {}; /** * @brief Handle a client connection. * This is called when a client connects. - * @param [in] pServer A pointer to the %BLE server that received the client connection. + * @param [in] pServer A pointer to the BLE server that received the client connection. * @param [in] connInfo A reference to a NimBLEConnInfo instance with information. * about the peer connection parameters. */ virtual void onConnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo); - /** - * @brief Handle a client connection. - * This is called when a client connects. - * @param [in] pServer A pointer to the %BLE server that received the client connection. - * @param [in] connInfo A reference to a NimBLEConnInfo instance with information. - * @param [in] name The name of the connected peer device. - * about the peer connection parameters. - */ - virtual void onConnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo, std::string& name); - /** * @brief Handle a client disconnection. - * This is called when a client discconnects. - * @param [in] pServer A pointer to the %BLE server that received the client disconnection. + * This is called when a client disconnects. + * @param [in] pServer A pointer to the BLE server that received the client disconnection. * @param [in] connInfo A reference to a NimBLEConnInfo instance with information * about the peer connection parameters. * @param [in] reason The reason code for the disconnection. @@ -179,32 +183,45 @@ public: /** * @brief Called when using numeric comparision for pairing. * @param [in] connInfo A reference to a NimBLEConnInfo instance with information - * Should be passed back to NimBLEDevice::injectConfirmPIN + * Should be passed back to NimBLEDevice::injectConfirmPasskey * @param [in] pin The pin to compare with the client. */ - virtual void onConfirmPIN(const NimBLEConnInfo& connInfo, uint32_t pin); + virtual void onConfirmPassKey(NimBLEConnInfo& connInfo, uint32_t pin); /** * @brief Called when the pairing procedure is complete. * @param [in] connInfo A reference to a NimBLEConnInfo instance with information * about the peer connection parameters. */ - virtual void onAuthenticationComplete(const NimBLEConnInfo& connInfo); - - /** - * @brief Called when the pairing procedure is complete. - * @param [in] connInfo A reference to a NimBLEConnInfo instance with information - * @param [in] name The name of the connected peer device. - * about the peer connection parameters. - */ - virtual void onAuthenticationComplete(const NimBLEConnInfo& connInfo, const std::string& name); + virtual void onAuthenticationComplete(NimBLEConnInfo& connInfo); /** * @brief Called when the peer identity address is resolved. * @param [in] connInfo A reference to a NimBLEConnInfo instance with information */ - virtual void onIdentity(const NimBLEConnInfo& connInfo); + virtual void onIdentity(NimBLEConnInfo& connInfo); + + /** + * @brief Called when connection parameters are updated following a request to + * update via NimBLEServer::updateConnParams + * @param [in] connInfo A reference to a NimBLEConnInfo instance containing the + * updated connection parameters. + */ + virtual void onConnParamsUpdate(NimBLEConnInfo& connInfo); + + /** + * @brief Called when the PHY update procedure is complete. + * @param [in] connInfo A reference to a NimBLEConnInfo instance with information + * about the peer connection parameters. + * @param [in] txPhy The transmit PHY. + * @param [in] rxPhy The receive PHY. + * Possible values: + * * BLE_GAP_LE_PHY_1M + * * BLE_GAP_LE_PHY_2M + * * BLE_GAP_LE_PHY_CODED + */ + virtual void onPhyUpdate(NimBLEConnInfo& connInfo, uint8_t txPhy, uint8_t rxPhy); }; // NimBLEServerCallbacks -#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL */ -#endif /* MAIN_NIMBLESERVER_H_ */ +#endif // CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL +#endif // NIMBLE_CPP_SERVER_H_ diff --git a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEService.cpp b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEService.cpp index 7c2decf01..db6c75fe2 100644 --- a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEService.cpp +++ b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEService.cpp @@ -1,107 +1,88 @@ /* - * NimBLEService.cpp + * Copyright 2020-2025 Ryan Powell and + * esp-nimble-cpp, NimBLE-Arduino contributors. * - * Created: on March 2, 2020 - * Author H2zero + * 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 * - * Originally: + * http://www.apache.org/licenses/LICENSE-2.0 * - * BLEService.cpp - * - * Created on: Mar 25, 2017 - * Author: kolban + * 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. */ -// A service is identified by a UUID. A service is also the container for one or more characteristics. - -#include "nimconfig.h" -#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) - -#include "NimBLEDevice.h" #include "NimBLEService.h" -#include "NimBLEUtils.h" -#include "NimBLELog.h" +#if CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL -#include +# if CONFIG_BT_NIMBLE_EXT_ADV +# include "NimBLEExtAdvertising.h" +# else +# include "NimBLEAdvertising.h" +# endif +# include "NimBLEDevice.h" +# include "NimBLEUtils.h" +# include "NimBLELog.h" -static const char* LOG_TAG = "NimBLEService"; // Tag for logging. - -#define NULL_HANDLE (0xffff) +# include +static const char* LOG_TAG = "NimBLEService"; /** * @brief Construct an instance of the NimBLEService * @param [in] uuid The UUID of the service. */ -NimBLEService::NimBLEService(const char* uuid) -: NimBLEService(NimBLEUUID(uuid)) { -} - +NimBLEService::NimBLEService(const char* uuid) : NimBLEService(NimBLEUUID(uuid)) {} /** * @brief Construct an instance of the BLEService * @param [in] uuid The UUID of the service. */ -NimBLEService::NimBLEService(const NimBLEUUID &uuid) { - m_uuid = uuid; - m_handle = NULL_HANDLE; - m_pSvcDef = nullptr; - m_removed = 0; - -} // NimBLEService - +NimBLEService::NimBLEService(const NimBLEUUID& uuid) + : NimBLELocalAttribute{uuid, 0}, m_pSvcDef{{0, getUUID().getBase(), nullptr, nullptr},{}} {} +/** + * @brief Destructor, make sure we release the resources allocated for the service. + */ NimBLEService::~NimBLEService() { - if(m_pSvcDef != nullptr) { - if(m_pSvcDef->characteristics != nullptr) { - for(int i=0; m_pSvcDef->characteristics[i].uuid != NULL; ++i) { - if(m_pSvcDef->characteristics[i].descriptors) { - delete(m_pSvcDef->characteristics[i].descriptors); - } - } - delete(m_pSvcDef->characteristics); + if (m_pSvcDef->characteristics) { + if (m_pSvcDef->characteristics->descriptors) { + delete[] m_pSvcDef->characteristics->descriptors; } - - delete(m_pSvcDef); + delete[] m_pSvcDef->characteristics; } - for(auto &it : m_chrVec) { + for (const auto& it : m_vChars) { delete it; } -} +} // ~NimBLEService /** * @brief Dump details of this BLE GATT service. */ -void NimBLEService::dump() { - NIMBLE_LOGD(LOG_TAG, "Service: uuid:%s, handle: 0x%2x", - m_uuid.toString().c_str(), - m_handle); +void NimBLEService::dump() const { + NIMBLE_LOGD(LOG_TAG, "Service: uuid:%s, handle: 0x%2x", getUUID().toString().c_str(), getHandle()); std::string res; - int count = 0; - char hex[5]; - for (auto &it: m_chrVec) { - if (count > 0) {res += "\n";} + int count = 0; + char hex[5]; + for (const auto& it : m_vChars) { + if (count > 0) { + res += "\n"; + } snprintf(hex, sizeof(hex), "%04x", it->getHandle()); count++; res += "handle: 0x"; res += hex; res += ", uuid: " + std::string(it->getUUID()); } + NIMBLE_LOGD(LOG_TAG, "Characteristics:\n%s", res.c_str()); } // dump - -/** - * @brief Get the UUID of the service. - * @return the UUID of the service. - */ -NimBLEUUID NimBLEService::getUUID() { - return m_uuid; -} // getUUID - - /** * @brief Builds the database of characteristics/descriptors for the service * and registers it with the NimBLE stack. @@ -110,150 +91,99 @@ NimBLEUUID NimBLEService::getUUID() { bool NimBLEService::start() { NIMBLE_LOGD(LOG_TAG, ">> start(): Starting service: %s", toString().c_str()); - // Rebuild the service definition if the server attributes have changed. - if(getServer()->m_svcChanged && m_pSvcDef != nullptr) { - if(m_pSvcDef[0].characteristics) { - if(m_pSvcDef[0].characteristics[0].descriptors) { - delete(m_pSvcDef[0].characteristics[0].descriptors); - } - delete(m_pSvcDef[0].characteristics); - } - delete(m_pSvcDef); - m_pSvcDef = nullptr; + // If started previously and no characteristics have been added or removed, + // then we can skip the service registration process. + if (m_pSvcDef->characteristics && !getServer()->m_svcChanged) { + return true; } - if(m_pSvcDef == nullptr) { - // Nimble requires an array of services to be sent to the api - // Since we are adding 1 at a time we create an array of 2 and set the type - // of the second service to 0 to indicate the end of the array. - ble_gatt_svc_def* svc = new ble_gatt_svc_def[2]{}; - ble_gatt_chr_def* pChr_a = nullptr; - ble_gatt_dsc_def* pDsc_a = nullptr; + // If started previously, clear everything and start over + if (m_pSvcDef->characteristics) { + if (m_pSvcDef->characteristics->descriptors) { + delete[] m_pSvcDef->characteristics->descriptors; + } + delete[] m_pSvcDef->characteristics; + } - svc[0].type = BLE_GATT_SVC_TYPE_PRIMARY; - svc[0].uuid = &m_uuid.getNative()->u; - svc[0].includes = NULL; + size_t numChrs = 0; + for (const auto& chr : m_vChars) { + if (chr->getRemoved()) { + continue; + } + ++numChrs; + } - int removedCount = 0; - for(auto it = m_chrVec.begin(); it != m_chrVec.end(); ) { - if ((*it)->m_removed > 0) { - if ((*it)->m_removed == NIMBLE_ATT_REMOVE_DELETE) { - delete *it; - it = m_chrVec.erase(it); - } else { - ++removedCount; - ++it; - } + NIMBLE_LOGD(LOG_TAG, "Adding %d characteristics for service %s", numChrs, toString().c_str()); + if (numChrs) { + int i = 0; + + // Nimble requires the last characteristic to have it's uuid = 0 to indicate the end + // of the characteristics for the service. We create 1 extra and set it to null + // for this purpose. + ble_gatt_chr_def* pChrs = new ble_gatt_chr_def[numChrs + 1]{}; + for (const auto& chr : m_vChars) { + if (chr->getRemoved()) { continue; } - ++it; - } - - size_t numChrs = m_chrVec.size() - removedCount; - NIMBLE_LOGD(LOG_TAG,"Adding %d characteristics for service %s", numChrs, toString().c_str()); - - if(!numChrs){ - svc[0].characteristics = NULL; - }else{ - // Nimble requires the last characteristic to have it's uuid = 0 to indicate the end - // of the characteristics for the service. We create 1 extra and set it to null - // for this purpose. - pChr_a = new ble_gatt_chr_def[numChrs + 1]{}; - int i = 0; - for(auto chr_it = m_chrVec.begin(); chr_it != m_chrVec.end(); ++chr_it) { - if((*chr_it)->m_removed > 0) { + size_t numDscs = 0; + for (const auto& dsc : chr->m_vDescriptors) { + if (dsc->getRemoved()) { continue; } + ++numDscs; + } - removedCount = 0; - for(auto it = (*chr_it)->m_dscVec.begin(); it != (*chr_it)->m_dscVec.end(); ) { - if ((*it)->m_removed > 0) { - if ((*it)->m_removed == NIMBLE_ATT_REMOVE_DELETE) { - delete *it; - it = (*chr_it)->m_dscVec.erase(it); - } else { - ++removedCount; - ++it; - } + if (numDscs) { + int j = 0; + + // Must have last descriptor uuid = 0 so we have to create 1 extra + ble_gatt_dsc_def* pDscs = new ble_gatt_dsc_def[numDscs + 1]{}; + for (const auto& dsc : chr->m_vDescriptors) { + if (dsc->getRemoved()) { continue; } - ++it; + pDscs[j].uuid = dsc->getUUID().getBase(); + pDscs[j].att_flags = dsc->getProperties(); + pDscs[j].min_key_size = 0; + pDscs[j].access_cb = NimBLEServer::handleGattEvent; + pDscs[j].arg = dsc; + ++j; } - size_t numDscs = (*chr_it)->m_dscVec.size() - removedCount; - - if(!numDscs){ - pChr_a[i].descriptors = NULL; - } else { - // Must have last descriptor uuid = 0 so we have to create 1 extra - pDsc_a = new ble_gatt_dsc_def[numDscs+1]{}; - int d = 0; - for(auto dsc_it = (*chr_it)->m_dscVec.begin(); dsc_it != (*chr_it)->m_dscVec.end(); ++dsc_it ) { - if((*dsc_it)->m_removed > 0) { - continue; - } - pDsc_a[d].uuid = &(*dsc_it)->m_uuid.getNative()->u; - pDsc_a[d].att_flags = (*dsc_it)->m_properties; - pDsc_a[d].min_key_size = 0; - pDsc_a[d].access_cb = NimBLEDescriptor::handleGapEvent; - pDsc_a[d].arg = (*dsc_it); - ++d; - } - - pDsc_a[numDscs].uuid = NULL; - pChr_a[i].descriptors = pDsc_a; - } - - pChr_a[i].uuid = &(*chr_it)->m_uuid.getNative()->u; - pChr_a[i].access_cb = NimBLECharacteristic::handleGapEvent; - pChr_a[i].arg = (*chr_it); - pChr_a[i].flags = (*chr_it)->m_properties; - pChr_a[i].min_key_size = 0; - pChr_a[i].val_handle = &(*chr_it)->m_handle; - ++i; + pChrs[i].descriptors = pDscs; } - pChr_a[numChrs].uuid = NULL; - svc[0].characteristics = pChr_a; + pChrs[i].uuid = chr->getUUID().getBase(); + pChrs[i].access_cb = NimBLEServer::handleGattEvent; + pChrs[i].arg = chr; + pChrs[i].flags = chr->getProperties(); + pChrs[i].min_key_size = 0; + pChrs[i].val_handle = &chr->m_handle; + ++i; } - // end of services must indicate to api with type = 0 - svc[1].type = 0; - m_pSvcDef = svc; + m_pSvcDef->characteristics = pChrs; } - int rc = ble_gatts_count_cfg((const ble_gatt_svc_def*)m_pSvcDef); + m_pSvcDef->type = BLE_GATT_SVC_TYPE_PRIMARY; + int rc = ble_gatts_count_cfg(m_pSvcDef); if (rc != 0) { NIMBLE_LOGE(LOG_TAG, "ble_gatts_count_cfg failed, rc= %d, %s", rc, NimBLEUtils::returnCodeToString(rc)); return false; } - rc = ble_gatts_add_svcs((const ble_gatt_svc_def*)m_pSvcDef); + rc = ble_gatts_add_svcs(m_pSvcDef); if (rc != 0) { NIMBLE_LOGE(LOG_TAG, "ble_gatts_add_svcs, rc= %d, %s", rc, NimBLEUtils::returnCodeToString(rc)); return false; - } NIMBLE_LOGD(LOG_TAG, "<< start()"); return true; } // start - -/** - * @brief Get the handle associated with this service. - * @return The handle associated with this service. - */ -uint16_t NimBLEService::getHandle() { - if (m_handle == NULL_HANDLE) { - ble_gatts_find_svc(&getUUID().getNative()->u, &m_handle); - } - return m_handle; -} // getHandle - - /** * @brief Create a new BLE Characteristic associated with this service. * @param [in] uuid - The UUID of the characteristic. @@ -263,8 +193,7 @@ uint16_t NimBLEService::getHandle() { */ NimBLECharacteristic* NimBLEService::createCharacteristic(const char* uuid, uint32_t properties, uint16_t max_len) { return createCharacteristic(NimBLEUUID(uuid), properties, max_len); -} - +} // createCharacteristic /** * @brief Create a new BLE Characteristic associated with this service. @@ -273,59 +202,62 @@ NimBLECharacteristic* NimBLEService::createCharacteristic(const char* uuid, uint * @param [in] max_len - The maximum length in bytes that the characteristic value can hold. * @return The new BLE characteristic. */ -NimBLECharacteristic* NimBLEService::createCharacteristic(const NimBLEUUID &uuid, uint32_t properties, uint16_t max_len) { - NimBLECharacteristic* pCharacteristic = new NimBLECharacteristic(uuid, properties, max_len, this); - +NimBLECharacteristic* NimBLEService::createCharacteristic(const NimBLEUUID& uuid, uint32_t properties, uint16_t max_len) { + NimBLECharacteristic* pChar = new NimBLECharacteristic(uuid, properties, max_len, this); if (getCharacteristic(uuid) != nullptr) { - NIMBLE_LOGD(LOG_TAG, "<< Adding a duplicate characteristic with UUID: %s", - std::string(uuid).c_str()); + NIMBLE_LOGD(LOG_TAG, "Adding a duplicate characteristic with UUID: %s", std::string(uuid).c_str()); } - addCharacteristic(pCharacteristic); - return pCharacteristic; + addCharacteristic(pChar); + return pChar; } // createCharacteristic - /** * @brief Add a characteristic to the service. - * @param[in] pCharacteristic A pointer to the characteristic instance to add to the service. + * @param[in] pChar A pointer to the characteristic instance to add to the service. */ -void NimBLEService::addCharacteristic(NimBLECharacteristic* pCharacteristic) { +void NimBLEService::addCharacteristic(NimBLECharacteristic* pChar) { bool foundRemoved = false; - - if(pCharacteristic->m_removed > 0) { - for(auto& it : m_chrVec) { - if(it == pCharacteristic) { + if (pChar->getRemoved() > 0) { + for (const auto& chr : m_vChars) { + if (chr == pChar) { foundRemoved = true; - pCharacteristic->m_removed = 0; + pChar->setRemoved(0); } } } - if(!foundRemoved) { - m_chrVec.push_back(pCharacteristic); + // Check if the characteristic is already in the service and if so, return. + for (const auto& chr : m_vChars) { + if (chr == pChar) { + pChar->setService(this); // Update the service pointer in the characteristic. + return; + } } - pCharacteristic->setService(this); + if (!foundRemoved) { + m_vChars.push_back(pChar); + } + + pChar->setService(this); getServer()->serviceChanged(); } // addCharacteristic - /** * @brief Remove a characteristic from the service. - * @param[in] pCharacteristic A pointer to the characteristic instance to remove from the service. + * @param[in] pChar A pointer to the characteristic instance to remove from the service. * @param[in] deleteChr If true it will delete the characteristic instance and free it's resources. */ -void NimBLEService::removeCharacteristic(NimBLECharacteristic* pCharacteristic, bool deleteChr) { +void NimBLEService::removeCharacteristic(NimBLECharacteristic* pChar, bool deleteChr) { // Check if the characteristic was already removed and if so, check if this // is being called to delete the object and do so if requested. // Otherwise, ignore the call and return. - if(pCharacteristic->m_removed > 0) { - if(deleteChr) { - for(auto it = m_chrVec.begin(); it != m_chrVec.end(); ++it) { - if ((*it) == pCharacteristic) { - m_chrVec.erase(it); - delete *it; + if (pChar->getRemoved() > 0) { + if (deleteChr) { + for (auto it = m_vChars.begin(); it != m_vChars.end(); ++it) { + if ((*it) == pChar) { + delete (*it); + m_vChars.erase(it); break; } } @@ -334,80 +266,82 @@ void NimBLEService::removeCharacteristic(NimBLECharacteristic* pCharacteristic, return; } - pCharacteristic->m_removed = deleteChr ? NIMBLE_ATT_REMOVE_DELETE : NIMBLE_ATT_REMOVE_HIDE; + pChar->setRemoved(deleteChr ? NIMBLE_ATT_REMOVE_DELETE : NIMBLE_ATT_REMOVE_HIDE); getServer()->serviceChanged(); } // removeCharacteristic +/** + * @brief Get a pointer to the characteristic object with the specified UUID. + * @param [in] uuid The UUID of the characteristic. + * @param idx The index of the characteristic to return (used when multiple characteristics have the same UUID). + * @return A pointer to the characteristic object or nullptr if not found. + */ +NimBLECharacteristic* NimBLEService::getCharacteristic(const char* uuid, uint16_t idx) const { + return getCharacteristic(NimBLEUUID(uuid), idx); +} // getCharacteristic /** * @brief Get a pointer to the characteristic object with the specified UUID. * @param [in] uuid The UUID of the characteristic. - * @param instanceId The index of the characteristic to return (used when multiple characteristics have the same UUID). + * @param idx The index of the characteristic to return (used when multiple characteristics have the same UUID). * @return A pointer to the characteristic object or nullptr if not found. */ -NimBLECharacteristic* NimBLEService::getCharacteristic(const char* uuid, uint16_t instanceId) { - return getCharacteristic(NimBLEUUID(uuid), instanceId); -} - -/** - * @brief Get a pointer to the characteristic object with the specified UUID. - * @param [in] uuid The UUID of the characteristic. - * @param instanceId The index of the characteristic to return (used when multiple characteristics have the same UUID). - * @return A pointer to the characteristic object or nullptr if not found. - */ -NimBLECharacteristic* NimBLEService::getCharacteristic(const NimBLEUUID &uuid, uint16_t instanceId) { +NimBLECharacteristic* NimBLEService::getCharacteristic(const NimBLEUUID& uuid, uint16_t idx) const { uint16_t position = 0; - for (auto &it : m_chrVec) { - if (it->getUUID() == uuid) { - if (position == instanceId) { - return it; + for (const auto& chr : m_vChars) { + if (chr->getUUID() == uuid) { + if (position == idx) { + return chr; } position++; } } + return nullptr; -} +} // getCharacteristic /** * @brief Get a pointer to the characteristic object with the specified handle. * @param handle The handle of the characteristic. * @return A pointer to the characteristic object or nullptr if not found. */ -NimBLECharacteristic *NimBLEService::getCharacteristicByHandle(uint16_t handle) { - for (auto &it : m_chrVec) { - if (it->getHandle() == handle) { - return it; +NimBLECharacteristic* NimBLEService::getCharacteristicByHandle(uint16_t handle) const { + for (const auto& chr : m_vChars) { + if (chr->getHandle() == handle) { + return chr; } } + return nullptr; -} +} // getCharacteristicByHandle /** * @return A vector containing pointers to each characteristic associated with this service. */ -std::vector NimBLEService::getCharacteristics() { - return m_chrVec; -} +const std::vector& NimBLEService::getCharacteristics() const { + return m_vChars; +} // getCharacteristics /** * @return A vector containing pointers to each characteristic with the provided UUID associated with this service. */ -std::vector NimBLEService::getCharacteristics(const char *uuid) { +std::vector NimBLEService::getCharacteristics(const char* uuid) const { return getCharacteristics(NimBLEUUID(uuid)); -} +} // getCharacteristics /** * @return A vector containing pointers to each characteristic with the provided UUID associated with this service. */ -std::vector NimBLEService::getCharacteristics(const NimBLEUUID &uuid) { +std::vector NimBLEService::getCharacteristics(const NimBLEUUID& uuid) const { std::vector result; - for (auto &it : m_chrVec) { - if (it->getUUID() == uuid) { - result.push_back(it); + for (const auto& chr : m_vChars) { + if (chr->getUUID() == uuid) { + result.push_back(chr); } } + return result; -} +} // getCharacteristics /** * @brief Return a string representation of this service. @@ -416,32 +350,29 @@ std::vector NimBLEService::getCharacteristics(const NimB * * Its handle * @return A string representation of this service. */ -std::string NimBLEService::toString() { +std::string NimBLEService::toString() const { std::string res = "UUID: " + getUUID().toString(); - char hex[5]; + char hex[5]; snprintf(hex, sizeof(hex), "%04x", getHandle()); res += ", handle: 0x"; res += hex; return res; } // toString - /** * @brief Get the BLE server associated with this service. * @return The BLEServer associated with this service. */ -NimBLEServer* NimBLEService::getServer() { +NimBLEServer* NimBLEService::getServer() const { return NimBLEDevice::getServer(); -}// getServer - +} // getServer /** * @brief Checks if the service has been started. * @return True if the service has been started. */ -bool NimBLEService::isStarted() { - return m_pSvcDef != nullptr; -} +bool NimBLEService::isStarted() const { + return m_pSvcDef->type > 0; +} // isStarted - -#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL */ +#endif // CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL diff --git a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEService.h b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEService.h index 73cbd87be..357f32cdc 100644 --- a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEService.h +++ b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEService.h @@ -1,87 +1,73 @@ /* - * NimBLEService.h + * Copyright 2020-2025 Ryan Powell and + * esp-nimble-cpp, NimBLE-Arduino contributors. * - * Created: on March 2, 2020 - * Author H2zero + * 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 * - * Originally: + * http://www.apache.org/licenses/LICENSE-2.0 * - * BLEService.h - * - * Created on: Mar 25, 2017 - * Author: kolban + * 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 MAIN_NIMBLESERVICE_H_ -#define MAIN_NIMBLESERVICE_H_ +#ifndef NIMBLE_CPP_SERVICE_H_ +#define NIMBLE_CPP_SERVICE_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 "NimBLEServer.h" -#include "NimBLECharacteristic.h" -#include "NimBLEUUID.h" - - -class NimBLEServer; -class NimBLECharacteristic; +class NimBLEService; +# include "NimBLEAttribute.h" +# include "NimBLEServer.h" +# include "NimBLECharacteristic.h" /** - * @brief The model of a %BLE service. + * @brief The model of a BLE service. * */ -class NimBLEService { -public: - +class NimBLEService : public NimBLELocalAttribute { + public: NimBLEService(const char* uuid); - NimBLEService(const NimBLEUUID &uuid); + NimBLEService(const NimBLEUUID& uuid); ~NimBLEService(); - NimBLEServer* getServer(); - - NimBLEUUID getUUID(); - uint16_t getHandle(); - std::string toString(); - void dump(); - bool isStarted(); + NimBLEServer* getServer() const; + std::string toString() const; + void dump() const; + bool isStarted() const; bool start(); - NimBLECharacteristic* createCharacteristic(const char* uuid, - uint32_t properties = - NIMBLE_PROPERTY::READ | - NIMBLE_PROPERTY::WRITE, - uint16_t max_len = BLE_ATT_ATTR_MAX_LEN); - - NimBLECharacteristic* createCharacteristic(const NimBLEUUID &uuid, - uint32_t properties = - NIMBLE_PROPERTY::READ | - NIMBLE_PROPERTY::WRITE, - uint16_t max_len = BLE_ATT_ATTR_MAX_LEN); + uint32_t properties = NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE, + uint16_t max_len = BLE_ATT_ATTR_MAX_LEN); + NimBLECharacteristic* createCharacteristic(const NimBLEUUID& uuid, + uint32_t properties = NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE, + uint16_t max_len = BLE_ATT_ATTR_MAX_LEN); void addCharacteristic(NimBLECharacteristic* pCharacteristic); void removeCharacteristic(NimBLECharacteristic* pCharacteristic, bool deleteChr = false); - NimBLECharacteristic* getCharacteristic(const char* uuid, uint16_t instanceId = 0); - NimBLECharacteristic* getCharacteristic(const NimBLEUUID &uuid, uint16_t instanceId = 0); - NimBLECharacteristic* getCharacteristicByHandle(uint16_t handle); + NimBLECharacteristic* getCharacteristic(const char* uuid, uint16_t instanceId = 0) const; + NimBLECharacteristic* getCharacteristic(const NimBLEUUID& uuid, uint16_t instanceId = 0) const; + NimBLECharacteristic* getCharacteristicByHandle(uint16_t handle) const; - std::vector getCharacteristics(); - std::vector getCharacteristics(const char* uuid); - std::vector getCharacteristics(const NimBLEUUID &uuid); + const std::vector& getCharacteristics() const; + std::vector getCharacteristics(const char* uuid) const; + std::vector getCharacteristics(const NimBLEUUID& uuid) const; + private: + friend class NimBLEServer; -private: - - friend class NimBLEServer; - friend class NimBLEDevice; - - uint16_t m_handle; - NimBLEUUID m_uuid; - ble_gatt_svc_def* m_pSvcDef; - uint8_t m_removed; - std::vector m_chrVec; - + std::vector m_vChars{}; + // Nimble requires an array of services to be sent to the api + // Since we are adding 1 at a time we create an array of 2 and set the type + // of the second service to 0 to indicate the end of the array. + ble_gatt_svc_def m_pSvcDef[2]{}; }; // NimBLEService -#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL */ -#endif /* MAIN_NIMBLESERVICE_H_ */ +#endif // CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL +#endif // NIMBLE_CPP_SERVICE_H_ diff --git a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEUUID.cpp b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEUUID.cpp index b14eae160..d26046339 100644 --- a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEUUID.cpp +++ b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEUUID.cpp @@ -1,35 +1,49 @@ /* - * NimBLEUUID.cpp + * Copyright 2020-2025 Ryan Powell and + * esp-nimble-cpp, NimBLE-Arduino contributors. * - * Created: on Jan 24 2020 - * Author H2zero + * 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 * - * Originally: + * http://www.apache.org/licenses/LICENSE-2.0 * - * BLEUUID.cpp - * - * Created on: Jun 21, 2017 - * Author: kolban + * 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 "nimconfig.h" -#if defined(CONFIG_BT_ENABLED) - -#include "NimBLEUtils.h" #include "NimBLEUUID.h" -#include "NimBLELog.h" +#if CONFIG_BT_ENABLED -#include +# include "NimBLEUtils.h" +# include "NimBLELog.h" -static const char* LOG_TAG = "NimBLEUUID"; +/**** FIX COMPILATION ****/ +# undef min +# undef max +/**************************/ +# include + +static const char* LOG_TAG = "NimBLEUUID"; +static const uint8_t ble_base_uuid[] = { + 0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +/** + * @brief Create a UUID from the native UUID. + * @param [in] uuid The native UUID. + */ +NimBLEUUID::NimBLEUUID(const ble_uuid_any_t& uuid) : m_uuid{uuid} {} /** * @brief Create a UUID from a string. * - * Create a UUID from a string. There will be two possible stories here. Either the string represents - * a binary data field or the string represents a hex encoding of a UUID. - * For the hex encoding, here is an example: + * Create a UUID from a string. There will be two possible stories here. Either + * the string represents a binary data field or the string represents a hex + * encoding of a UUID. For the hex encoding, here is an example: * * ``` * "beb5483e-36e1-4688-b7f5-ea07361b26a8" @@ -41,104 +55,68 @@ static const char* LOG_TAG = "NimBLEUUID"; * * @param [in] value The string to build a UUID from. */ - NimBLEUUID::NimBLEUUID(const std::string &value) { - m_valueSet = true; +NimBLEUUID::NimBLEUUID(const std::string& value) { if (value.length() == 4) { - m_uuid.u.type = BLE_UUID_TYPE_16; - m_uuid.u16.value = strtoul(value.c_str(), NULL, 16); - } - else if (value.length() == 8) { - m_uuid.u.type = BLE_UUID_TYPE_32; - m_uuid.u32.value = strtoul(value.c_str(), NULL, 16); - } - else if (value.length() == 16) { - *this = NimBLEUUID((uint8_t*)value.data(), 16, true); - } - else if (value.length() == 36) { - // If the length of the string is 36 bytes then we will assume it is a long hex string in - // UUID format. - char * position = const_cast(value.c_str()); - uint32_t first = strtoul(position, &position, 16); - uint16_t second = strtoul(position + 1, &position, 16); - uint16_t third = strtoul(position + 1, &position, 16); - uint16_t fourth = strtoul(position + 1, &position, 16); - uint64_t fifth = strtoull(position + 1, NULL, 16); - *this = NimBLEUUID(first, second, third, (uint64_t(fourth) << 48) + fifth); - } - else { - m_valueSet = false; + m_uuid.u.type = BLE_UUID_TYPE_16; + m_uuid.u16.value = strtoul(value.c_str(), nullptr, 16); + } else if (value.length() == 8) { + m_uuid.u.type = BLE_UUID_TYPE_32; + m_uuid.u32.value = strtoul(value.c_str(), nullptr, 16); + } else if (value.length() == 16) { + memcpy(m_uuid.u128.value, &value[0], 16); + m_uuid.u.type = BLE_UUID_TYPE_128; + } else if (value.length() == 36) { + char* position; + uint64_t first_half = (strtoull(&value[0], &position, 16) << 32) + + (strtoull(position + 1, &position, 16) << 16) + strtoull(position + 1, &position, 16); + uint64_t second_half = (strtoull(position + 1, &position, 16) << 48) + strtoull(position + 1, nullptr, 16); + memcpy(m_uuid.u128.value + 8, &first_half, 8); + memcpy(m_uuid.u128.value, &second_half, 8); + m_uuid.u.type = BLE_UUID_TYPE_128; + } else { + NIMBLE_LOGE(LOG_TAG, "Invalid UUID length"); + m_uuid.u.type = 0; } } // NimBLEUUID(std::string) - /** * @brief Create a UUID from 2, 4, 16 bytes of memory. * @param [in] pData The pointer to the start of the UUID. * @param [in] size The size of the data. - * @param [in] msbFirst Is the MSB first in pData memory? */ -NimBLEUUID::NimBLEUUID(const uint8_t* pData, size_t size, bool msbFirst) { - uint8_t *uuidValue = nullptr; - - switch(size) { - case 2: - uuidValue = (uint8_t*)&m_uuid.u16.value; - m_uuid.u.type = BLE_UUID_TYPE_16; - break; - case 4: - uuidValue = (uint8_t*)&m_uuid.u32.value; - m_uuid.u.type = BLE_UUID_TYPE_32; - break; - case 16: - uuidValue = m_uuid.u128.value; - m_uuid.u.type = BLE_UUID_TYPE_128; - break; - default: - m_valueSet = false; - NIMBLE_LOGE(LOG_TAG, "Invalid UUID size"); - return; +NimBLEUUID::NimBLEUUID(const uint8_t* pData, size_t size) { + if (ble_uuid_init_from_buf(&m_uuid, pData, size)) { + NIMBLE_LOGE(LOG_TAG, "Invalid UUID size"); + m_uuid.u.type = 0; } - if (msbFirst) { - std::reverse_copy(pData, pData + size, uuidValue); - } else { - memcpy(uuidValue, pData, size); - } - m_valueSet = true; -} // NimBLEUUID - +} // NimBLEUUID(const uint8_t* pData, size_t size) /** * @brief Create a UUID from the 16bit value. * @param [in] uuid The 16bit short form UUID. */ NimBLEUUID::NimBLEUUID(uint16_t uuid) { - m_uuid.u.type = BLE_UUID_TYPE_16; - m_uuid.u16.value = uuid; - m_valueSet = true; -} // NimBLEUUID - + m_uuid.u.type = BLE_UUID_TYPE_16; + m_uuid.u16.value = uuid; +} // NimBLEUUID(uint16_t uuid) /** * @brief Create a UUID from the 32bit value. * @param [in] uuid The 32bit short form UUID. */ NimBLEUUID::NimBLEUUID(uint32_t uuid) { - m_uuid.u.type = BLE_UUID_TYPE_32; - m_uuid.u32.value = uuid; - m_valueSet = true; -} // NimBLEUUID - + m_uuid.u.type = BLE_UUID_TYPE_32; + m_uuid.u32.value = uuid; +} // NimBLEUUID(uint32_t uuid) /** * @brief Create a UUID from the native UUID. * @param [in] uuid The native UUID. */ NimBLEUUID::NimBLEUUID(const ble_uuid128_t* uuid) { - m_uuid.u.type = BLE_UUID_TYPE_128; + m_uuid.u.type = BLE_UUID_TYPE_128; memcpy(m_uuid.u128.value, uuid->value, 16); - m_valueSet = true; -} // NimBLEUUID - +} // NimBLEUUID(const ble_uuid128_t* uuid) /** * @brief Create a UUID from the 128bit value using hex parts instead of string, @@ -151,32 +129,47 @@ NimBLEUUID::NimBLEUUID(const ble_uuid128_t* uuid) { * @param [in] fourth The last 64bit of the UUID, combining the last 2 parts of the string equivalent */ NimBLEUUID::NimBLEUUID(uint32_t first, uint16_t second, uint16_t third, uint64_t fourth) { - m_uuid.u.type = BLE_UUID_TYPE_128; - memcpy(m_uuid.u128.value + 12, &first, 4); + m_uuid.u.type = BLE_UUID_TYPE_128; + memcpy(m_uuid.u128.value + 12, &first, 4); memcpy(m_uuid.u128.value + 10, &second, 2); - memcpy(m_uuid.u128.value + 8, &third, 2); - memcpy(m_uuid.u128.value, &fourth, 8); - m_valueSet = true; -} - + memcpy(m_uuid.u128.value + 8, &third, 2); + memcpy(m_uuid.u128.value, &fourth, 8); +} // NimBLEUUID(uint32_t first, uint16_t second, uint16_t third, uint64_t fourth) /** - * @brief Creates an empty UUID. - */ -NimBLEUUID::NimBLEUUID() { - m_valueSet = false; -} // NimBLEUUID - - -/** - * @brief Get the number of bits in this uuid. - * @return The number of bits in the UUID. One of 16, 32 or 128. + * @brief Get the bit size of the UUID, 16, 32 or 128. + * @return The bit size of the UUID or 0 if not initialized. */ uint8_t NimBLEUUID::bitSize() const { - if (!m_valueSet) return 0; - return m_uuid.u.type; + return this->m_uuid.u.type; } // bitSize +/** + * @brief Get the uuid value. + * @return A pointer to the UUID value or nullptr if not initialized. + * @note This should be checked with `bitSize()` before accessing. + */ +const uint8_t* NimBLEUUID::getValue() const { + switch (bitSize()) { + case BLE_UUID_TYPE_16: + return reinterpret_cast(&m_uuid.u16.value); + case BLE_UUID_TYPE_32: + return reinterpret_cast(&m_uuid.u32.value); + case BLE_UUID_TYPE_128: + return m_uuid.u128.value; + default: + return nullptr; + } +} // getValue + +/** + * @brief Get a pointer to the NimBLE UUID base structure. + * @return A Read-only pointer to the NimBLE UUID base structure. + * @note The type value should be checked, if no 16, 32 or 128 then the UUID is not initialized. + */ +const ble_uuid_t* NimBLEUUID::getBase() const { + return &this->m_uuid.u; +} // getBase /** * @brief Compare a UUID against this UUID. @@ -184,10 +177,9 @@ uint8_t NimBLEUUID::bitSize() const { * @param [in] uuid The UUID to compare against. * @return True if the UUIDs are equal and false otherwise. */ -bool NimBLEUUID::equals(const NimBLEUUID &uuid) const { +bool NimBLEUUID::equals(const NimBLEUUID& uuid) const { return *this == uuid; -} - +} // equals /** * Create a NimBLEUUID from a string of the form: @@ -200,14 +192,14 @@ bool NimBLEUUID::equals(const NimBLEUUID &uuid) const { * * @param [in] uuid The string to create the UUID from. */ -NimBLEUUID NimBLEUUID::fromString(const std::string &uuid) { +NimBLEUUID NimBLEUUID::fromString(const std::string& uuid) { uint8_t start = 0; if (strstr(uuid.c_str(), "0x") != nullptr) { // If the string starts with 0x, skip those characters. start = 2; } - uint8_t len = uuid.length() - start; // Calculate the length of the string we are going to use. - if(len == 4) { + uint8_t len = uuid.length() - start; // Calculate the length of the string we are going to use. + if (len == 4) { uint16_t x = strtoul(uuid.substr(start, len).c_str(), NULL, 16); return NimBLEUUID(x); } else if (len == 8) { @@ -216,47 +208,29 @@ NimBLEUUID NimBLEUUID::fromString(const std::string &uuid) { } else if (len == 36) { return NimBLEUUID(uuid); } + return NimBLEUUID(); } // fromString - -/** - * @brief Get the native UUID value. - * @return The native UUID value or nullptr if not set. - */ -const ble_uuid_any_t* NimBLEUUID::getNative() const { - if (m_valueSet == false) { - NIMBLE_LOGD(LOG_TAG,"<< Return of un-initialized UUID!"); - return nullptr; - } - return &m_uuid; -} // getNative - - /** * @brief Convert a UUID to its 128 bit representation. * @details A UUID can be internally represented as 16bit, 32bit or the full 128bit. * This method will convert 16 or 32bit representations to the full 128bit. * @return The NimBLEUUID converted to 128bit. */ -const NimBLEUUID &NimBLEUUID::to128() { +const NimBLEUUID& NimBLEUUID::to128() { // If we either don't have a value or are already a 128 bit UUID, nothing further to do. - if (!m_valueSet || m_uuid.u.type == BLE_UUID_TYPE_128) { - return *this; - } - - // If we are 16 bit or 32 bit, then set the other bytes of the UUID. - if (m_uuid.u.type == BLE_UUID_TYPE_16) { - *this = NimBLEUUID(m_uuid.u16.value, 0x0000, 0x1000, 0x800000805f9b34fb); - } - else if (m_uuid.u.type == BLE_UUID_TYPE_32) { - *this = NimBLEUUID(m_uuid.u32.value, 0x0000, 0x1000, 0x800000805f9b34fb); + if (bitSize() != BLE_UUID_TYPE_128) { + uint32_t val = bitSize() == BLE_UUID_TYPE_16 ? *reinterpret_cast(getValue()) + : *reinterpret_cast(getValue()); + memcpy(m_uuid.u128.value, &ble_base_uuid, sizeof(ble_base_uuid) - 4); + memcpy(m_uuid.u128.value + 12, &val, 4); + m_uuid.u.type = BLE_UUID_TYPE_128; } return *this; } // to128 - /** * @brief Convert 128 bit UUID to its 16 bit representation. * @details A UUID can be internally represented as 16bit, 32bit or the full 128bit. @@ -264,21 +238,16 @@ const NimBLEUUID &NimBLEUUID::to128() { * @return The NimBLEUUID converted to 16bit if successful, otherwise the original uuid. */ const NimBLEUUID& NimBLEUUID::to16() { - if (!m_valueSet || m_uuid.u.type == BLE_UUID_TYPE_16) { - return *this; - } - - if (m_uuid.u.type == BLE_UUID_TYPE_128) { - uint8_t base128[] = {0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, - 0x00, 0x80, 0x00, 0x10, 0x00, 0x00}; - if (memcmp(m_uuid.u128.value, base128, sizeof(base128)) == 0 ) { - *this = NimBLEUUID(*(uint16_t*)(m_uuid.u128.value + 12)); + if (bitSize() == BLE_UUID_TYPE_128) { + const uint8_t* val = getValue(); + if (memcmp(val, ble_base_uuid, sizeof(ble_base_uuid) - 4) == 0) { + m_uuid.u16.value = *reinterpret_cast(val + 12); + m_uuid.u.type = BLE_UUID_TYPE_16; } } return *this; -} - +} // to16 /** * @brief Get a string representation of the UUID. @@ -295,53 +264,67 @@ std::string NimBLEUUID::toString() const { return std::string(*this); } // toString +/** + * @brief Reverse the byte order of the UUID. + * @return The NimBLEUUID with the byte order reversed. + * @details This is useful when comparing UUIDs or when the advertisement data is reversed. + */ +const NimBLEUUID& NimBLEUUID::reverseByteOrder() { + if (bitSize() == BLE_UUID_TYPE_128) { + std::reverse(m_uuid.u128.value, m_uuid.u128.value + 16); + } else if (bitSize() == BLE_UUID_TYPE_32) { + m_uuid.u32.value = __builtin_bswap32(m_uuid.u32.value); + } else if (bitSize() == BLE_UUID_TYPE_16) { + m_uuid.u16.value = __builtin_bswap16(m_uuid.u16.value); + } + + return *this; +} // reverseByteOrder /** * @brief Convenience operator to check if this UUID is equal to another. */ -bool NimBLEUUID::operator ==(const NimBLEUUID & rhs) const { - if(m_valueSet && rhs.m_valueSet) { - if(m_uuid.u.type != rhs.m_uuid.u.type) { - uint8_t uuidBase[16] = { - 0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80, - 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 - }; - - if(m_uuid.u.type == BLE_UUID_TYPE_128){ - if(rhs.m_uuid.u.type == BLE_UUID_TYPE_16){ - memcpy(uuidBase+12, &rhs.m_uuid.u16.value, 2); - } else if (rhs.m_uuid.u.type == BLE_UUID_TYPE_32){ - memcpy(uuidBase+12, &rhs.m_uuid.u32.value, 4); - } - return memcmp(m_uuid.u128.value,uuidBase,16) == 0; - - } else if(rhs.m_uuid.u.type == BLE_UUID_TYPE_128) { - if(m_uuid.u.type == BLE_UUID_TYPE_16){ - memcpy(uuidBase+12, &m_uuid.u16.value, 2); - } else if (m_uuid.u.type == BLE_UUID_TYPE_32){ - memcpy(uuidBase+12, &m_uuid.u32.value, 4); - } - return memcmp(rhs.m_uuid.u128.value,uuidBase,16) == 0; - - } else { - return false; - } - } - - return ble_uuid_cmp(&m_uuid.u, &rhs.m_uuid.u) == 0; +bool NimBLEUUID::operator==(const NimBLEUUID& rhs) const { + if (!this->bitSize() || !rhs.bitSize()) { + return false; } - return m_valueSet == rhs.m_valueSet; -} + if (this->bitSize() != rhs.bitSize()) { + uint8_t uuid128[sizeof(ble_base_uuid)]; + memcpy(uuid128, &ble_base_uuid, sizeof(ble_base_uuid)); + if (this->bitSize() == BLE_UUID_TYPE_128) { + memcpy(uuid128 + 12, rhs.getValue(), rhs.bitSize() == BLE_UUID_TYPE_16 ? 2 : 4); + return memcmp(this->getValue(), uuid128, sizeof(ble_base_uuid)) == 0; + } else if (rhs.bitSize() == BLE_UUID_TYPE_128) { + memcpy(uuid128 + 12, getValue(), this->bitSize() == BLE_UUID_TYPE_16 ? 2 : 4); + return memcmp(rhs.getValue(), uuid128, sizeof(ble_base_uuid)) == 0; + } else { + return false; + } + } + + if (this->bitSize() == BLE_UUID_TYPE_16) { + return *reinterpret_cast(this->getValue()) == *reinterpret_cast(rhs.getValue()); + } + + if (this->bitSize() == BLE_UUID_TYPE_32) { + return *reinterpret_cast(this->getValue()) == *reinterpret_cast(rhs.getValue()); + } + + if (this->bitSize() == BLE_UUID_TYPE_128) { + return memcmp(this->getValue(), rhs.getValue(), 16) == 0; + } + + return false; +} // operator== /** * @brief Convenience operator to check if this UUID is not equal to another. */ -bool NimBLEUUID::operator !=(const NimBLEUUID & rhs) const { +bool NimBLEUUID::operator!=(const NimBLEUUID& rhs) const { return !this->operator==(rhs); -} - +} // operator!= /** * @brief Convenience operator to convert this UUID to string representation. @@ -349,12 +332,8 @@ bool NimBLEUUID::operator !=(const NimBLEUUID & rhs) const { * that accept std::string and/or or it's methods as a parameter. */ NimBLEUUID::operator std::string() const { - if (!m_valueSet) return std::string(); // If we have no value, nothing to format. - char buf[BLE_UUID_STR_LEN]; - return ble_uuid_to_str(&m_uuid.u, buf); -} - +} // operator std::string #endif /* CONFIG_BT_ENABLED */ diff --git a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEUUID.h b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEUUID.h index 2c24971f0..39b58b978 100644 --- a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEUUID.h +++ b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEUUID.h @@ -1,64 +1,74 @@ /* - * NimBLEUUID.h + * Copyright 2020-2025 Ryan Powell and + * esp-nimble-cpp, NimBLE-Arduino contributors. * - * Created: on Jan 24 2020 - * Author H2zero + * 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 * - * Originally: + * http://www.apache.org/licenses/LICENSE-2.0 * - * BLEUUID.h - * - * Created on: Jun 21, 2017 - * Author: kolban + * 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 COMPONENTS_NIMBLEUUID_H_ -#define COMPONENTS_NIMBLEUUID_H_ +#ifndef NIMBLE_CPP_UUID_H_ +#define NIMBLE_CPP_UUID_H_ #include "nimconfig.h" -#if defined(CONFIG_BT_ENABLED) +#if CONFIG_BT_ENABLED -#if defined(CONFIG_NIMBLE_CPP_IDF) -#include "host/ble_uuid.h" -#else -#include "nimble/nimble/host/include/host/ble_uuid.h" -#endif +# if defined(CONFIG_NIMBLE_CPP_IDF) +# include "host/ble_uuid.h" +# else +# include "nimble/nimble/host/include/host/ble_uuid.h" +# endif /**** FIX COMPILATION ****/ -#undef min -#undef max +# undef min +# undef max /**************************/ -#include +# include +# include /** * @brief A model of a %BLE UUID. */ class NimBLEUUID { -public: - NimBLEUUID(const std::string &uuid); + public: + /** + * @brief Created a blank UUID. + */ + NimBLEUUID() = default; + NimBLEUUID(const ble_uuid_any_t& uuid); + NimBLEUUID(const std::string& uuid); NimBLEUUID(uint16_t uuid); NimBLEUUID(uint32_t uuid); NimBLEUUID(const ble_uuid128_t* uuid); - NimBLEUUID(const uint8_t* pData, size_t size, bool msbFirst); + NimBLEUUID(const uint8_t* pData, size_t size); NimBLEUUID(uint32_t first, uint16_t second, uint16_t third, uint64_t fourth); - NimBLEUUID(); - uint8_t bitSize() const; - bool equals(const NimBLEUUID &uuid) const; - const ble_uuid_any_t* getNative() const; - const NimBLEUUID & to128(); - const NimBLEUUID& to16(); - std::string toString() const; - static NimBLEUUID fromString(const std::string &uuid); + uint8_t bitSize() const; + const uint8_t* getValue() const; + const ble_uuid_t* getBase() const; + bool equals(const NimBLEUUID& uuid) const; + std::string toString() const; + static NimBLEUUID fromString(const std::string& uuid); + const NimBLEUUID& to128(); + const NimBLEUUID& to16(); + const NimBLEUUID& reverseByteOrder(); - bool operator ==(const NimBLEUUID & rhs) const; - bool operator !=(const NimBLEUUID & rhs) const; + bool operator==(const NimBLEUUID& rhs) const; + bool operator!=(const NimBLEUUID& rhs) const; operator std::string() const; -private: - ble_uuid_any_t m_uuid; - bool m_valueSet = false; + private: + ble_uuid_any_t m_uuid{}; }; // NimBLEUUID -#endif /* CONFIG_BT_ENABLED */ -#endif /* COMPONENTS_NIMBLEUUID_H_ */ + +#endif // CONFIG_BT_ENABLED +#endif // NIMBLE_CPP_UUID_H_ diff --git a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEUtils.cpp b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEUtils.cpp index 06e649c09..f51d68238 100644 --- a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEUtils.cpp +++ b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEUtils.cpp @@ -1,479 +1,579 @@ /* - * NimBLEUtils.cpp + * Copyright 2020-2025 Ryan Powell and + * esp-nimble-cpp, NimBLE-Arduino contributors. * - * Created: on Jan 25 2020 - * Author H2zero + * 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 "nimconfig.h" -#if defined(CONFIG_BT_ENABLED) - #include "NimBLEUtils.h" -#include "NimBLELog.h" +#if CONFIG_BT_ENABLED -#include +# include "NimBLEAddress.h" +# include "NimBLELog.h" + +# if defined(CONFIG_NIMBLE_CPP_IDF) +# include "host/ble_hs.h" +# else +# include "nimble/nimble/host/include/host/ble_hs.h" +# endif + +/**** FIX COMPILATION ****/ +# undef min +# undef max +/**************************/ + +# include +# include + +# if defined INC_FREERTOS_H +# ifndef CONFIG_NIMBLE_CPP_FREERTOS_TASK_BLOCK_BIT +# define CONFIG_NIMBLE_CPP_FREERTOS_TASK_BLOCK_BIT 31 +# endif +constexpr uint32_t TASK_BLOCK_BIT = (1 << CONFIG_NIMBLE_CPP_FREERTOS_TASK_BLOCK_BIT); +# endif static const char* LOG_TAG = "NimBLEUtils"; +/** + * @brief Construct a NimBLETaskData instance. + * @param [in] pInstance An instance of the class that will be waiting. + * @param [in] flags General purpose flags for the caller. + * @param [in] buf A buffer for data. + */ +NimBLETaskData::NimBLETaskData(void* pInstance, int flags, void* buf) + : m_pInstance{pInstance}, + m_flags{flags}, + m_pBuf{buf} +# if defined INC_FREERTOS_H + , + m_pHandle{xTaskGetCurrentTaskHandle()} { +} +# else +{ + ble_npl_sem* sem = new ble_npl_sem; + if (ble_npl_sem_init(sem, 0) != BLE_NPL_OK) { + NIMBLE_LOGE(LOG_TAG, "Failed to init semaphore"); + delete sem; + m_pHandle = nullptr; + } else { + m_pHandle = sem; + } +} +# endif + +/** + * @brief Destructor. + */ +NimBLETaskData::~NimBLETaskData() { +# if !defined INC_FREERTOS_H + if (m_pHandle != nullptr) { + ble_npl_sem_deinit(static_cast(m_pHandle)); + delete static_cast(m_pHandle); + } +# endif +} + +/** + * @brief Blocks the calling task until released or timeout. + * @param [in] taskData A pointer to the task data structure. + * @param [in] timeout The time to wait in milliseconds. + * @return True if the task completed, false if the timeout was reached. + */ +bool NimBLEUtils::taskWait(const NimBLETaskData& taskData, uint32_t timeout) { + ble_npl_time_t ticks; + if (timeout == BLE_NPL_TIME_FOREVER) { + ticks = BLE_NPL_TIME_FOREVER; + } else { + ble_npl_time_ms_to_ticks(timeout, &ticks); + } + +# if defined INC_FREERTOS_H + uint32_t notificationValue; + xTaskNotifyWait(0, TASK_BLOCK_BIT, ¬ificationValue, 0); + if (notificationValue & TASK_BLOCK_BIT) { + return true; + } + + return xTaskNotifyWait(0, TASK_BLOCK_BIT, nullptr, ticks) == pdTRUE; + +# else + return ble_npl_sem_pend(static_cast(taskData.m_pHandle), ticks) == BLE_NPL_OK; +# endif +} // taskWait + +/** + * @brief Release a task. + * @param [in] taskData A pointer to the task data structure. + * @param [in] flags A return value to set in the task data structure. + */ +void NimBLEUtils::taskRelease(const NimBLETaskData& taskData, int flags) { + taskData.m_flags = flags; + if (taskData.m_pHandle != nullptr) { +# if defined INC_FREERTOS_H + xTaskNotify(static_cast(taskData.m_pHandle), TASK_BLOCK_BIT, eSetBits); +# else + ble_npl_sem_release(static_cast(taskData.m_pHandle)); +# endif + } +} // taskRelease + /** * @brief Converts a return code from the NimBLE stack to a text string. * @param [in] rc The return code to convert. * @return A string representation of the return code. */ const char* NimBLEUtils::returnCodeToString(int rc) { -#if defined(CONFIG_NIMBLE_CPP_ENABLE_RETURN_CODE_TEXT) - switch(rc) { +# if defined(CONFIG_NIMBLE_CPP_ENABLE_RETURN_CODE_TEXT) + switch (rc) { case 0: return "SUCCESS"; case BLE_HS_EAGAIN: - return "Temporary failure; try again."; + return "Temporary failure; try again"; case BLE_HS_EALREADY: - return "Operation already in progress or completed."; + return "Operation already in progress or complete"; case BLE_HS_EINVAL: - return "One or more arguments are invalid."; + return "One or more arguments are invalid"; case BLE_HS_EMSGSIZE: - return "The provided buffer is too small."; + return "Buffer too small"; case BLE_HS_ENOENT: - return "No entry matching the specified criteria."; + return "No matching entry found"; case BLE_HS_ENOMEM: - return "Operation failed due to resource exhaustion."; + return "Not enough memory"; case BLE_HS_ENOTCONN: - return "No open connection with the specified handle."; + return "No open connection with handle"; case BLE_HS_ENOTSUP: - return "Operation disabled at compile time."; + return "Operation disabled at compile time"; case BLE_HS_EAPP: - return "Application callback behaved unexpectedly."; + return "Operation canceled by app"; case BLE_HS_EBADDATA: - return "Command from peer is invalid."; + return "Invalid command from peer"; case BLE_HS_EOS: - return "Mynewt OS error."; + return "OS error"; case BLE_HS_ECONTROLLER: - return "Event from controller is invalid."; + return "Controller error"; case BLE_HS_ETIMEOUT: - return "Operation timed out."; + return "Operation timed out"; case BLE_HS_EDONE: - return "Operation completed successfully."; + return "Operation completed successfully"; case BLE_HS_EBUSY: - return "Operation cannot be performed until procedure completes."; + return "Busy"; case BLE_HS_EREJECT: - return "Peer rejected a connection parameter update request."; + return "Peer rejected request"; case BLE_HS_EUNKNOWN: - return "Unexpected failure; catch all."; + return "Unknown failure"; case BLE_HS_EROLE: - return "Operation requires different role (e.g., central vs. peripheral)."; + return "Operation requires different role"; case BLE_HS_ETIMEOUT_HCI: - return "HCI request timed out; controller unresponsive."; + return "HCI request timed out"; case BLE_HS_ENOMEM_EVT: - return "Controller failed to send event due to memory exhaustion (combined host-controller only)."; + return "Controller error; not enough memory"; case BLE_HS_ENOADDR: - return "Operation requires an identity address but none configured."; + return "No identity address"; case BLE_HS_ENOTSYNCED: - return "Attempt to use the host before it is synced with controller."; + return "Host not synced with controller"; case BLE_HS_EAUTHEN: - return "Insufficient authentication."; + return "Insufficient authentication"; case BLE_HS_EAUTHOR: - return "Insufficient authorization."; + return "Insufficient authorization"; case BLE_HS_EENCRYPT: - return "Insufficient encryption level."; + return "Insufficient encryption level"; case BLE_HS_EENCRYPT_KEY_SZ: - return "Insufficient key size."; + return "Insufficient key size"; case BLE_HS_ESTORE_CAP: - return "Storage at capacity."; + return "Storage at capacity"; case BLE_HS_ESTORE_FAIL: - return "Storage IO error."; - case (0x0100+BLE_ATT_ERR_INVALID_HANDLE ): - return "The attribute handle given was not valid on this server."; - case (0x0100+BLE_ATT_ERR_READ_NOT_PERMITTED ): - return "The attribute cannot be read."; - case (0x0100+BLE_ATT_ERR_WRITE_NOT_PERMITTED ): - return "The attribute cannot be written."; - case (0x0100+BLE_ATT_ERR_INVALID_PDU ): - return "The attribute PDU was invalid."; - case (0x0100+BLE_ATT_ERR_INSUFFICIENT_AUTHEN ): - return "The attribute requires authentication before it can be read or written."; - case (0x0100+BLE_ATT_ERR_REQ_NOT_SUPPORTED ): - return "Attribute server does not support the request received from the client."; - case (0x0100+BLE_ATT_ERR_INVALID_OFFSET ): - return "Offset specified was past the end of the attribute."; - case (0x0100+BLE_ATT_ERR_INSUFFICIENT_AUTHOR ): - return "The attribute requires authorization before it can be read or written."; - case (0x0100+BLE_ATT_ERR_PREPARE_QUEUE_FULL ): - return "Too many prepare writes have been queued."; - case (0x0100+BLE_ATT_ERR_ATTR_NOT_FOUND ): - return "No attribute found within the given attribute handle range."; - case (0x0100+BLE_ATT_ERR_ATTR_NOT_LONG ): - return "The attribute cannot be read or written using the Read Blob Request."; - case (0x0100+BLE_ATT_ERR_INSUFFICIENT_KEY_SZ ): - return "The Encryption Key Size used for encrypting this link is insufficient."; - case (0x0100+BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN ): - return "The attribute value length is invalid for the operation."; - case (0x0100+BLE_ATT_ERR_UNLIKELY ): - return "The attribute request has encountered an error that was unlikely, could not be completed as requested."; - case (0x0100+BLE_ATT_ERR_INSUFFICIENT_ENC ): - return "The attribute requires encryption before it can be read or written."; - case (0x0100+BLE_ATT_ERR_UNSUPPORTED_GROUP ): - return "The attribute type is not a supported grouping attribute as defined by a higher layer specification."; - case (0x0100+BLE_ATT_ERR_INSUFFICIENT_RES ): - return "Insufficient Resources to complete the request."; - case (0x0200+BLE_ERR_UNKNOWN_HCI_CMD ): + return "Storage IO error"; + case BLE_HS_EPREEMPTED: + return "Host preempted"; + case BLE_HS_EDISABLED: + return "Host disabled"; + case BLE_HS_ESTALLED: + return "CoC module is stalled"; + case (BLE_HS_ERR_ATT_BASE + BLE_ATT_ERR_INVALID_HANDLE): + return "Invalid attribute handle"; + case (BLE_HS_ERR_ATT_BASE + BLE_ATT_ERR_READ_NOT_PERMITTED): + return "The attribute cannot be read"; + case (BLE_HS_ERR_ATT_BASE + BLE_ATT_ERR_WRITE_NOT_PERMITTED): + return "The attribute cannot be written"; + case (BLE_HS_ERR_ATT_BASE + BLE_ATT_ERR_INVALID_PDU): + return "Invalid attribute PDU"; + case (BLE_HS_ERR_ATT_BASE + BLE_ATT_ERR_INSUFFICIENT_AUTHEN): + return "Insufficient authentication"; + case (BLE_HS_ERR_ATT_BASE + BLE_ATT_ERR_REQ_NOT_SUPPORTED): + return "Unsupported request"; + case (BLE_HS_ERR_ATT_BASE + BLE_ATT_ERR_INVALID_OFFSET): + return "Invalid offset"; + case (BLE_HS_ERR_ATT_BASE + BLE_ATT_ERR_INSUFFICIENT_AUTHOR): + return "Insufficient authorization"; + case (BLE_HS_ERR_ATT_BASE + BLE_ATT_ERR_PREPARE_QUEUE_FULL): + return "Prepare write queue full"; + case (BLE_HS_ERR_ATT_BASE + BLE_ATT_ERR_ATTR_NOT_FOUND): + return "Attribute not found"; + case (BLE_HS_ERR_ATT_BASE + BLE_ATT_ERR_ATTR_NOT_LONG): + return "Long read not supported"; + case (BLE_HS_ERR_ATT_BASE + BLE_ATT_ERR_INSUFFICIENT_KEY_SZ): + return "Insufficient encryption key size"; + case (BLE_HS_ERR_ATT_BASE + BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN): + return "Invalid attribute value length"; + case (BLE_HS_ERR_ATT_BASE + BLE_ATT_ERR_UNLIKELY): + return "Unlikely attribute error"; + case (BLE_HS_ERR_ATT_BASE + BLE_ATT_ERR_INSUFFICIENT_ENC): + return "Insufficient encryption"; + case (BLE_HS_ERR_ATT_BASE + BLE_ATT_ERR_UNSUPPORTED_GROUP): + return "Not a supported grouping attribute type"; + case (BLE_HS_ERR_ATT_BASE + BLE_ATT_ERR_INSUFFICIENT_RES): + return "Insufficient Resources"; + case (BLE_HS_ERR_HCI_BASE + BLE_ERR_UNKNOWN_HCI_CMD): return "Unknown HCI Command"; - case (0x0200+BLE_ERR_UNK_CONN_ID ): + case (BLE_HS_ERR_HCI_BASE + BLE_ERR_UNK_CONN_ID): return "Unknown Connection Identifier"; - case (0x0200+BLE_ERR_HW_FAIL ): + case (BLE_HS_ERR_HCI_BASE + BLE_ERR_HW_FAIL): return "Hardware Failure"; - case (0x0200+BLE_ERR_PAGE_TMO ): + case (BLE_HS_ERR_HCI_BASE + BLE_ERR_PAGE_TMO): return "Page Timeout"; - case (0x0200+BLE_ERR_AUTH_FAIL ): + case (BLE_HS_ERR_HCI_BASE + BLE_ERR_AUTH_FAIL): return "Authentication Failure"; - case (0x0200+BLE_ERR_PINKEY_MISSING ): + case (BLE_HS_ERR_HCI_BASE + BLE_ERR_PINKEY_MISSING): return "PIN or Key Missing"; - case (0x0200+BLE_ERR_MEM_CAPACITY ): + case (BLE_HS_ERR_HCI_BASE + BLE_ERR_MEM_CAPACITY): return "Memory Capacity Exceeded"; - case (0x0200+BLE_ERR_CONN_SPVN_TMO ): + case (BLE_HS_ERR_HCI_BASE + BLE_ERR_CONN_SPVN_TMO): return "Connection Timeout"; - case (0x0200+BLE_ERR_CONN_LIMIT ): + case (BLE_HS_ERR_HCI_BASE + BLE_ERR_CONN_LIMIT): return "Connection Limit Exceeded"; - case (0x0200+BLE_ERR_SYNCH_CONN_LIMIT ): + case (BLE_HS_ERR_HCI_BASE + BLE_ERR_SYNCH_CONN_LIMIT): return "Synchronous Connection Limit To A Device Exceeded"; - case (0x0200+BLE_ERR_ACL_CONN_EXISTS ): + case (BLE_HS_ERR_HCI_BASE + BLE_ERR_ACL_CONN_EXISTS): return "ACL Connection Already Exists"; - case (0x0200+BLE_ERR_CMD_DISALLOWED ): + case (BLE_HS_ERR_HCI_BASE + BLE_ERR_CMD_DISALLOWED): return "Command Disallowed"; - case (0x0200+BLE_ERR_CONN_REJ_RESOURCES ): + case (BLE_HS_ERR_HCI_BASE + BLE_ERR_CONN_REJ_RESOURCES): return "Connection Rejected due to Limited Resources"; - case (0x0200+BLE_ERR_CONN_REJ_SECURITY ): + case (BLE_HS_ERR_HCI_BASE + BLE_ERR_CONN_REJ_SECURITY): return "Connection Rejected Due To Security Reasons"; - case (0x0200+BLE_ERR_CONN_REJ_BD_ADDR ): + case (BLE_HS_ERR_HCI_BASE + BLE_ERR_CONN_REJ_BD_ADDR): return "Connection Rejected due to Unacceptable BD_ADDR"; - case (0x0200+BLE_ERR_CONN_ACCEPT_TMO ): + case (BLE_HS_ERR_HCI_BASE + BLE_ERR_CONN_ACCEPT_TMO): return "Connection Accept Timeout Exceeded"; - case (0x0200+BLE_ERR_UNSUPPORTED ): + case (BLE_HS_ERR_HCI_BASE + BLE_ERR_UNSUPPORTED): return "Unsupported Feature or Parameter Value"; - case (0x0200+BLE_ERR_INV_HCI_CMD_PARMS ): + case (BLE_HS_ERR_HCI_BASE + BLE_ERR_INV_HCI_CMD_PARMS): return "Invalid HCI Command Parameters"; - case (0x0200+BLE_ERR_REM_USER_CONN_TERM ): + case (BLE_HS_ERR_HCI_BASE + BLE_ERR_REM_USER_CONN_TERM): return "Remote User Terminated Connection"; - case (0x0200+BLE_ERR_RD_CONN_TERM_RESRCS ): - return "Remote Device Terminated Connection due to Low Resources"; - case (0x0200+BLE_ERR_RD_CONN_TERM_PWROFF ): - return "Remote Device Terminated Connection due to Power Off"; - case (0x0200+BLE_ERR_CONN_TERM_LOCAL ): + case (BLE_HS_ERR_HCI_BASE + BLE_ERR_RD_CONN_TERM_RESRCS): + return "Remote Device Terminated Connection; Low Resources"; + case (BLE_HS_ERR_HCI_BASE + BLE_ERR_RD_CONN_TERM_PWROFF): + return "Remote Device Terminated Connection; Power Off"; + case (BLE_HS_ERR_HCI_BASE + BLE_ERR_CONN_TERM_LOCAL): return "Connection Terminated By Local Host"; - case (0x0200+BLE_ERR_REPEATED_ATTEMPTS ): - return "Repeated Attempts"; - case (0x0200+BLE_ERR_NO_PAIRING ): + case (BLE_HS_ERR_HCI_BASE + BLE_ERR_REPEATED_ATTEMPTS): + return "Repeated Pairing Attempts"; + case (BLE_HS_ERR_HCI_BASE + BLE_ERR_NO_PAIRING): return "Pairing Not Allowed"; - case (0x0200+BLE_ERR_UNK_LMP ): + case (BLE_HS_ERR_HCI_BASE + BLE_ERR_UNK_LMP): return "Unknown LMP PDU"; - case (0x0200+BLE_ERR_UNSUPP_REM_FEATURE ): - return "Unsupported Remote Feature / Unsupported LMP Feature"; - case (0x0200+BLE_ERR_SCO_OFFSET ): + case (BLE_HS_ERR_HCI_BASE + BLE_ERR_UNSUPP_REM_FEATURE): + return "Unsupported Remote Feature"; + case (BLE_HS_ERR_HCI_BASE + BLE_ERR_SCO_OFFSET): return "SCO Offset Rejected"; - case (0x0200+BLE_ERR_SCO_ITVL ): + case (BLE_HS_ERR_HCI_BASE + BLE_ERR_SCO_ITVL): return "SCO Interval Rejected"; - case (0x0200+BLE_ERR_SCO_AIR_MODE ): + case (BLE_HS_ERR_HCI_BASE + BLE_ERR_SCO_AIR_MODE): return "SCO Air Mode Rejected"; - case (0x0200+BLE_ERR_INV_LMP_LL_PARM ): - return "Invalid LMP Parameters / Invalid LL Parameters"; - case (0x0200+BLE_ERR_UNSPECIFIED ): + case (BLE_HS_ERR_HCI_BASE + BLE_ERR_INV_LMP_LL_PARM): + return "Invalid LL Parameters"; + case (BLE_HS_ERR_HCI_BASE + BLE_ERR_UNSPECIFIED): return "Unspecified Error"; - case (0x0200+BLE_ERR_UNSUPP_LMP_LL_PARM ): - return "Unsupported LMP Parameter Value / Unsupported LL Parameter Value"; - case (0x0200+BLE_ERR_NO_ROLE_CHANGE ): + case (BLE_HS_ERR_HCI_BASE + BLE_ERR_UNSUPP_LMP_LL_PARM): + return "Unsupported LL Parameter Value"; + case (BLE_HS_ERR_HCI_BASE + BLE_ERR_NO_ROLE_CHANGE): return "Role Change Not Allowed"; - case (0x0200+BLE_ERR_LMP_LL_RSP_TMO ): - return "LMP Response Timeout / LL Response Timeout"; - case (0x0200+BLE_ERR_LMP_COLLISION ): + case (BLE_HS_ERR_HCI_BASE + BLE_ERR_LMP_LL_RSP_TMO): + return "LL Response Timeout"; + case (BLE_HS_ERR_HCI_BASE + BLE_ERR_LMP_COLLISION): return "LMP Error Transaction Collision"; - case (0x0200+BLE_ERR_LMP_PDU ): + case (BLE_HS_ERR_HCI_BASE + BLE_ERR_LMP_PDU): return "LMP PDU Not Allowed"; - case (0x0200+BLE_ERR_ENCRYPTION_MODE ): + case (BLE_HS_ERR_HCI_BASE + BLE_ERR_ENCRYPTION_MODE): return "Encryption Mode Not Acceptable"; - case (0x0200+BLE_ERR_LINK_KEY_CHANGE ): + case (BLE_HS_ERR_HCI_BASE + BLE_ERR_LINK_KEY_CHANGE): return "Link Key cannot be Changed"; - case (0x0200+BLE_ERR_UNSUPP_QOS ): + case (BLE_HS_ERR_HCI_BASE + BLE_ERR_UNSUPP_QOS): return "Requested QoS Not Supported"; - case (0x0200+BLE_ERR_INSTANT_PASSED ): + case (BLE_HS_ERR_HCI_BASE + BLE_ERR_INSTANT_PASSED): return "Instant Passed"; - case (0x0200+BLE_ERR_UNIT_KEY_PAIRING ): + case (BLE_HS_ERR_HCI_BASE + BLE_ERR_UNIT_KEY_PAIRING): return "Pairing With Unit Key Not Supported"; - case (0x0200+BLE_ERR_DIFF_TRANS_COLL ): + case (BLE_HS_ERR_HCI_BASE + BLE_ERR_DIFF_TRANS_COLL): return "Different Transaction Collision"; - case (0x0200+BLE_ERR_QOS_PARM ): + case (BLE_HS_ERR_HCI_BASE + BLE_ERR_QOS_PARM): return "QoS Unacceptable Parameter"; - case (0x0200+BLE_ERR_QOS_REJECTED ): + case (BLE_HS_ERR_HCI_BASE + BLE_ERR_QOS_REJECTED): return "QoS Rejected"; - case (0x0200+BLE_ERR_CHAN_CLASS ): + case (BLE_HS_ERR_HCI_BASE + BLE_ERR_CHAN_CLASS): return "Channel Classification Not Supported"; - case (0x0200+BLE_ERR_INSUFFICIENT_SEC ): + case (BLE_HS_ERR_HCI_BASE + BLE_ERR_INSUFFICIENT_SEC): return "Insufficient Security"; - case (0x0200+BLE_ERR_PARM_OUT_OF_RANGE ): + case (BLE_HS_ERR_HCI_BASE + BLE_ERR_PARM_OUT_OF_RANGE): return "Parameter Out Of Mandatory Range"; - case (0x0200+BLE_ERR_PENDING_ROLE_SW ): + case (BLE_HS_ERR_HCI_BASE + BLE_ERR_PENDING_ROLE_SW): return "Role Switch Pending"; - case (0x0200+BLE_ERR_RESERVED_SLOT ): + case (BLE_HS_ERR_HCI_BASE + BLE_ERR_RESERVED_SLOT): return "Reserved Slot Violation"; - case (0x0200+BLE_ERR_ROLE_SW_FAIL ): + case (BLE_HS_ERR_HCI_BASE + BLE_ERR_ROLE_SW_FAIL): return "Role Switch Failed"; - case (0x0200+BLE_ERR_INQ_RSP_TOO_BIG ): + case (BLE_HS_ERR_HCI_BASE + BLE_ERR_INQ_RSP_TOO_BIG): return "Extended Inquiry Response Too Large"; - case (0x0200+BLE_ERR_SEC_SIMPLE_PAIR ): + case (BLE_HS_ERR_HCI_BASE + BLE_ERR_SEC_SIMPLE_PAIR): return "Secure Simple Pairing Not Supported By Host"; - case (0x0200+BLE_ERR_HOST_BUSY_PAIR ): + case (BLE_HS_ERR_HCI_BASE + BLE_ERR_HOST_BUSY_PAIR): return "Host Busy - Pairing"; - case (0x0200+BLE_ERR_CONN_REJ_CHANNEL ): + case (BLE_HS_ERR_HCI_BASE + BLE_ERR_CONN_REJ_CHANNEL): return "Connection Rejected, No Suitable Channel Found"; - case (0x0200+BLE_ERR_CTLR_BUSY ): + case (BLE_HS_ERR_HCI_BASE + BLE_ERR_CTLR_BUSY): return "Controller Busy"; - case (0x0200+BLE_ERR_CONN_PARMS ): + case (BLE_HS_ERR_HCI_BASE + BLE_ERR_CONN_PARMS): return "Unacceptable Connection Parameters"; - case (0x0200+BLE_ERR_DIR_ADV_TMO ): + case (BLE_HS_ERR_HCI_BASE + BLE_ERR_DIR_ADV_TMO): return "Directed Advertising Timeout"; - case (0x0200+BLE_ERR_CONN_TERM_MIC ): - return "Connection Terminated due to MIC Failure"; - case (0x0200+BLE_ERR_CONN_ESTABLISHMENT ): + case (BLE_HS_ERR_HCI_BASE + BLE_ERR_CONN_TERM_MIC): + return "Connection Terminated; MIC Failure"; + case (BLE_HS_ERR_HCI_BASE + BLE_ERR_CONN_ESTABLISHMENT): return "Connection Failed to be Established"; - case (0x0200+BLE_ERR_MAC_CONN_FAIL ): + case (BLE_HS_ERR_HCI_BASE + BLE_ERR_MAC_CONN_FAIL): return "MAC Connection Failed"; - case (0x0200+BLE_ERR_COARSE_CLK_ADJ ): + case (BLE_HS_ERR_HCI_BASE + BLE_ERR_COARSE_CLK_ADJ): return "Coarse Clock Adjustment Rejected"; - case (0x0300+BLE_L2CAP_SIG_ERR_CMD_NOT_UNDERSTOOD ): - return "Invalid or unsupported incoming L2CAP sig command."; - case (0x0300+BLE_L2CAP_SIG_ERR_MTU_EXCEEDED ): - return "Incoming packet too large."; - case (0x0300+BLE_L2CAP_SIG_ERR_INVALID_CID ): - return "No channel with specified ID."; - case (0x0400+BLE_SM_ERR_PASSKEY ): - return "The user input of passkey failed, for example, the user cancelled the operation."; - case (0x0400+BLE_SM_ERR_OOB ): - return "The OOB data is not available."; - case (0x0400+BLE_SM_ERR_AUTHREQ ): - return "The pairing procedure cannot be performed as authentication requirements cannot be met due to IO capabilities of one or both devices."; - case (0x0400+BLE_SM_ERR_CONFIRM_MISMATCH ): - return "The confirm value does not match the calculated compare value."; - case (0x0400+BLE_SM_ERR_PAIR_NOT_SUPP ): - return "Pairing is not supported by the device."; - case (0x0400+BLE_SM_ERR_ENC_KEY_SZ ): - return "The resultant encryption key size is insufficient for the security requirements of this device."; - case (0x0400+BLE_SM_ERR_CMD_NOT_SUPP ): - return "The SMP command received is not supported on this device."; - case (0x0400+BLE_SM_ERR_UNSPECIFIED ): - return "Pairing failed due to an unspecified reason."; - case (0x0400+BLE_SM_ERR_REPEATED ): - return "Pairing or authentication procedure disallowed, too little time has elapsed since last pairing request or security request."; - case (0x0400+BLE_SM_ERR_INVAL ): - return "Command length is invalid or that a parameter is outside of the specified range."; - case (0x0400+BLE_SM_ERR_DHKEY ): - return "DHKey Check value received doesn't match the one calculated by the local device."; - case (0x0400+BLE_SM_ERR_NUMCMP ): - return "Confirm values in the numeric comparison protocol do not match."; - case (0x0400+BLE_SM_ERR_ALREADY ): - return "Pairing over the LE transport failed - Pairing Request sent over the BR/EDR transport in process."; - case (0x0400+BLE_SM_ERR_CROSS_TRANS ): - return "BR/EDR Link Key generated on the BR/EDR transport cannot be used to derive and distribute keys for the LE transport."; - case (0x0500+BLE_SM_ERR_PASSKEY ): - return "The user input of passkey failed or the user cancelled the operation."; - case (0x0500+BLE_SM_ERR_OOB ): - return "The OOB data is not available."; - case (0x0500+BLE_SM_ERR_AUTHREQ ): - return "The pairing procedure cannot be performed as authentication requirements cannot be met due to IO capabilities of one or both devices."; - case (0x0500+BLE_SM_ERR_CONFIRM_MISMATCH ): - return "The confirm value does not match the calculated compare value."; - case (0x0500+BLE_SM_ERR_PAIR_NOT_SUPP ): - return "Pairing is not supported by the device."; - case (0x0500+BLE_SM_ERR_ENC_KEY_SZ ): - return "The resultant encryption key size is insufficient for the security requirements of this device."; - case (0x0500+BLE_SM_ERR_CMD_NOT_SUPP ): - return "The SMP command received is not supported on this device."; - case (0x0500+BLE_SM_ERR_UNSPECIFIED ): - return "Pairing failed due to an unspecified reason."; - case (0x0500+BLE_SM_ERR_REPEATED ): - return "Pairing or authentication procedure is disallowed because too little time has elapsed since last pairing request or security request."; - case (0x0500+BLE_SM_ERR_INVAL ): - return "Command length is invalid or a parameter is outside of the specified range."; - case (0x0500+BLE_SM_ERR_DHKEY ): - return "Indicates to the remote device that the DHKey Check value received doesn’t match the one calculated by the local device."; - case (0x0500+BLE_SM_ERR_NUMCMP ): - return "Confirm values in the numeric comparison protocol do not match."; - case (0x0500+BLE_SM_ERR_ALREADY ): - return "Pairing over the LE transport failed - Pairing Request sent over the BR/EDR transport in process."; - case (0x0500+BLE_SM_ERR_CROSS_TRANS ): - return "BR/EDR Link Key generated on the BR/EDR transport cannot be used to derive and distribute keys for the LE transport."; + case (BLE_HS_ERR_HCI_BASE + BLE_ERR_TYPE0_SUBMAP_NDEF): + return "Type0 Submap Not Defined"; + case (BLE_HS_ERR_HCI_BASE + BLE_ERR_UNK_ADV_INDENT): + return "Unknown Advertising Identifier"; + case (BLE_HS_ERR_HCI_BASE + BLE_ERR_LIMIT_REACHED): + return "Limit Reached"; + case (BLE_HS_ERR_HCI_BASE + BLE_ERR_OPERATION_CANCELLED): + return "Operation Cancelled by Host"; + case (BLE_HS_ERR_HCI_BASE + BLE_ERR_PACKET_TOO_LONG): + return "Packet Too Long"; + case (BLE_HS_ERR_L2C_BASE + BLE_L2CAP_SIG_ERR_CMD_NOT_UNDERSTOOD): + return "Invalid or unsupported incoming L2CAP sig command"; + case (BLE_HS_ERR_L2C_BASE + BLE_L2CAP_SIG_ERR_MTU_EXCEEDED): + return "Incoming packet too large"; + case (BLE_HS_ERR_L2C_BASE + BLE_L2CAP_SIG_ERR_INVALID_CID): + return "No channel with specified ID"; + case (BLE_HS_ERR_SM_US_BASE + BLE_SM_ERR_PASSKEY): + case (BLE_HS_ERR_SM_PEER_BASE + BLE_SM_ERR_PASSKEY): + return "Incorrect passkey or the user cancelled"; + case (BLE_HS_ERR_SM_US_BASE + BLE_SM_ERR_OOB): + case (BLE_HS_ERR_SM_PEER_BASE + BLE_SM_ERR_OOB): + return "The OOB data is not available"; + case (BLE_HS_ERR_SM_US_BASE + BLE_SM_ERR_AUTHREQ): + case (BLE_HS_ERR_SM_PEER_BASE + BLE_SM_ERR_AUTHREQ): + return "Authentication requirements cannot be met due to IO capabilities"; + case (BLE_HS_ERR_SM_US_BASE + BLE_SM_ERR_CONFIRM_MISMATCH): + case (BLE_HS_ERR_SM_PEER_BASE + BLE_SM_ERR_CONFIRM_MISMATCH): + return "The confirm value does not match the calculated compare value"; + case (BLE_HS_ERR_SM_US_BASE + BLE_SM_ERR_PAIR_NOT_SUPP): + case (BLE_HS_ERR_SM_PEER_BASE + BLE_SM_ERR_PAIR_NOT_SUPP): + return "Pairing is not supported by the device"; + case (BLE_HS_ERR_SM_US_BASE + BLE_SM_ERR_ENC_KEY_SZ): + case (BLE_HS_ERR_SM_PEER_BASE + BLE_SM_ERR_ENC_KEY_SZ): + return "Insufficient encryption key size for this device"; + case (BLE_HS_ERR_SM_US_BASE + BLE_SM_ERR_CMD_NOT_SUPP): + case (BLE_HS_ERR_SM_PEER_BASE + BLE_SM_ERR_CMD_NOT_SUPP): + return "The SMP command received is not supported on this device"; + case (BLE_HS_ERR_SM_US_BASE + BLE_SM_ERR_UNSPECIFIED): + case (BLE_HS_ERR_SM_PEER_BASE + BLE_SM_ERR_UNSPECIFIED): + return "Pairing failed; unspecified reason"; + case (BLE_HS_ERR_SM_US_BASE + BLE_SM_ERR_REPEATED): + case (BLE_HS_ERR_SM_PEER_BASE + BLE_SM_ERR_REPEATED): + return "Repeated pairing attempt"; + case (BLE_HS_ERR_SM_US_BASE + BLE_SM_ERR_INVAL): + case (BLE_HS_ERR_SM_PEER_BASE + BLE_SM_ERR_INVAL): + return "Invalid command length or parameter"; + case (BLE_HS_ERR_SM_US_BASE + BLE_SM_ERR_DHKEY): + case (BLE_HS_ERR_SM_PEER_BASE + BLE_SM_ERR_DHKEY): + return "DHKey check value received doesn't match"; + case (BLE_HS_ERR_SM_US_BASE + BLE_SM_ERR_NUMCMP): + case (BLE_HS_ERR_SM_PEER_BASE + BLE_SM_ERR_NUMCMP): + return "Confirm values do not match"; + case (BLE_HS_ERR_SM_US_BASE + BLE_SM_ERR_ALREADY): + case (BLE_HS_ERR_SM_PEER_BASE + BLE_SM_ERR_ALREADY): + return "Pairing already in process"; + case (BLE_HS_ERR_SM_US_BASE + BLE_SM_ERR_CROSS_TRANS): + case (BLE_HS_ERR_SM_PEER_BASE + BLE_SM_ERR_CROSS_TRANS): + return "Invalid link key for the LE transport"; default: return "Unknown"; } -#else // #if defined(CONFIG_NIMBLE_CPP_ENABLE_RETURN_CODE_TEXT) +# else // #if defined(CONFIG_NIMBLE_CPP_ENABLE_RETURN_CODE_TEXT) (void)rc; return ""; -#endif // #if defined(CONFIG_NIMBLE_CPP_ENABLE_RETURN_CODE_TEXT) +# endif // #if defined(CONFIG_NIMBLE_CPP_ENABLE_RETURN_CODE_TEXT) } - /** * @brief Convert the advertising type flag to a string. * @param advType The type to convert. * @return A string representation of the advertising flags. */ const char* NimBLEUtils::advTypeToString(uint8_t advType) { -#if defined(CONFIG_NIMBLE_CPP_ENABLE_ADVERTISEMENT_TYPE_TEXT) - switch(advType) { - case BLE_HCI_ADV_TYPE_ADV_IND : //0 +# if defined(CONFIG_NIMBLE_CPP_ENABLE_ADVERTISEMENT_TYPE_TEXT) + switch (advType) { + case BLE_HCI_ADV_TYPE_ADV_IND: // 0 return "Undirected - Connectable / Scannable"; - case BLE_HCI_ADV_TYPE_ADV_DIRECT_IND_HD: //1 + case BLE_HCI_ADV_TYPE_ADV_DIRECT_IND_HD: // 1 return "Directed High Duty - Connectable"; - case BLE_HCI_ADV_TYPE_ADV_SCAN_IND: //2 + case BLE_HCI_ADV_TYPE_ADV_SCAN_IND: // 2 return "Non-Connectable - Scan Response Available"; - case BLE_HCI_ADV_TYPE_ADV_NONCONN_IND: //3 + case BLE_HCI_ADV_TYPE_ADV_NONCONN_IND: // 3 return "Non-Connectable - No Scan Response"; - case BLE_HCI_ADV_TYPE_ADV_DIRECT_IND_LD: //4 + case BLE_HCI_ADV_TYPE_ADV_DIRECT_IND_LD: // 4 return "Directed Low Duty - Connectable"; default: return "Unknown flag"; } -#else // #if defined(CONFIG_NIMBLE_CPP_ENABLE_ADVERTISEMENT_TYPE_TEXT) +# else // #if defined(CONFIG_NIMBLE_CPP_ENABLE_ADVERTISEMENT_TYPE_TEXT) (void)advType; return ""; -#endif // #if defined(CONFIG_NIMBLE_CPP_ENABLE_ADVERTISEMENT_TYPE_TEXT) +# endif // #if defined(CONFIG_NIMBLE_CPP_ENABLE_ADVERTISEMENT_TYPE_TEXT) } // adFlagsToString - -/** - * @brief Create a hex representation of data. - * - * @param [in] target Where to write the hex string. If this is null, we malloc storage. - * @param [in] source The start of the binary data. - * @param [in] length The length of the data to convert. - * @return A pointer to the formatted buffer. - */ -char* NimBLEUtils::buildHexData(uint8_t* target, const uint8_t* source, uint8_t length) { - // Guard against too much data. - if (length > 100) length = 100; - - if (target == nullptr) { - target = (uint8_t*) malloc(length * 2 + 1); - if (target == nullptr) { - NIMBLE_LOGE(LOG_TAG, "buildHexData: malloc failed"); - return nullptr; - } - } - char* startOfData = (char*) target; - - for (int i = 0; i < length; i++) { - sprintf((char*) target, "%.2x", (char) *source); - source++; - target += 2; - } - - // Handle the special case where there was no data. - if (length == 0) { - *startOfData = 0; - } - - return startOfData; -} // buildHexData - - -/** - * @brief Utility function to log the gap event info. - * @param [in] event A pointer to the gap event structure. - * @param [in] arg Unused. - */ -void NimBLEUtils::dumpGapEvent(ble_gap_event *event, void *arg){ - (void)arg; -#if defined(CONFIG_NIMBLE_CPP_ENABLE_GAP_EVENT_CODE_TEXT) - NIMBLE_LOGD(LOG_TAG, "Received a GAP event: %s", gapEventToString(event->type)); -#else - (void)event; -#endif -} - - /** * @brief Convert a GAP event type to a string representation. * @param [in] eventType The type of event. * @return A string representation of the event type. */ const char* NimBLEUtils::gapEventToString(uint8_t eventType) { -#if defined(CONFIG_NIMBLE_CPP_ENABLE_GAP_EVENT_CODE_TEXT) +# if defined(CONFIG_NIMBLE_CPP_ENABLE_GAP_EVENT_CODE_TEXT) switch (eventType) { - case BLE_GAP_EVENT_CONNECT : //0 + case BLE_GAP_EVENT_CONNECT: // 0 return "BLE_GAP_EVENT_CONNECT "; - - case BLE_GAP_EVENT_DISCONNECT: //1 + case BLE_GAP_EVENT_DISCONNECT: // 1 return "BLE_GAP_EVENT_DISCONNECT"; - - case BLE_GAP_EVENT_CONN_UPDATE: //3 + case BLE_GAP_EVENT_CONN_UPDATE: // 3 return "BLE_GAP_EVENT_CONN_UPDATE"; - - case BLE_GAP_EVENT_CONN_UPDATE_REQ: //4 + case BLE_GAP_EVENT_CONN_UPDATE_REQ: // 4 return "BLE_GAP_EVENT_CONN_UPDATE_REQ"; - - case BLE_GAP_EVENT_L2CAP_UPDATE_REQ: //5 + case BLE_GAP_EVENT_L2CAP_UPDATE_REQ: // 5 return "BLE_GAP_EVENT_L2CAP_UPDATE_REQ"; - - case BLE_GAP_EVENT_TERM_FAILURE: //6 + case BLE_GAP_EVENT_TERM_FAILURE: // 6 return "BLE_GAP_EVENT_TERM_FAILURE"; - - case BLE_GAP_EVENT_DISC: //7 + case BLE_GAP_EVENT_DISC: // 7 return "BLE_GAP_EVENT_DISC"; - - case BLE_GAP_EVENT_DISC_COMPLETE: //8 + case BLE_GAP_EVENT_DISC_COMPLETE: // 8 return "BLE_GAP_EVENT_DISC_COMPLETE"; - - case BLE_GAP_EVENT_ADV_COMPLETE: //9 + case BLE_GAP_EVENT_ADV_COMPLETE: // 9 return "BLE_GAP_EVENT_ADV_COMPLETE"; - - case BLE_GAP_EVENT_ENC_CHANGE: //10 + case BLE_GAP_EVENT_ENC_CHANGE: // 10 return "BLE_GAP_EVENT_ENC_CHANGE"; - - case BLE_GAP_EVENT_PASSKEY_ACTION : //11 + case BLE_GAP_EVENT_PASSKEY_ACTION: // 11 return "BLE_GAP_EVENT_PASSKEY_ACTION"; - - case BLE_GAP_EVENT_NOTIFY_RX: //12 + case BLE_GAP_EVENT_NOTIFY_RX: // 12 return "BLE_GAP_EVENT_NOTIFY_RX"; - - case BLE_GAP_EVENT_NOTIFY_TX : //13 + case BLE_GAP_EVENT_NOTIFY_TX: // 13 return "BLE_GAP_EVENT_NOTIFY_TX"; - - case BLE_GAP_EVENT_SUBSCRIBE : //14 + case BLE_GAP_EVENT_SUBSCRIBE: // 14 return "BLE_GAP_EVENT_SUBSCRIBE"; - - case BLE_GAP_EVENT_MTU: //15 + case BLE_GAP_EVENT_MTU: // 15 return "BLE_GAP_EVENT_MTU"; - - case BLE_GAP_EVENT_IDENTITY_RESOLVED: //16 + case BLE_GAP_EVENT_IDENTITY_RESOLVED: // 16 return "BLE_GAP_EVENT_IDENTITY_RESOLVED"; - - case BLE_GAP_EVENT_REPEAT_PAIRING: //17 + case BLE_GAP_EVENT_REPEAT_PAIRING: // 17 return "BLE_GAP_EVENT_REPEAT_PAIRING"; - - case BLE_GAP_EVENT_PHY_UPDATE_COMPLETE: //18 + case BLE_GAP_EVENT_PHY_UPDATE_COMPLETE: // 18 return "BLE_GAP_EVENT_PHY_UPDATE_COMPLETE"; - - case BLE_GAP_EVENT_EXT_DISC: //19 + case BLE_GAP_EVENT_EXT_DISC: // 19 return "BLE_GAP_EVENT_EXT_DISC"; -#ifdef BLE_GAP_EVENT_PERIODIC_SYNC // IDF 4.0 does not support these - case BLE_GAP_EVENT_PERIODIC_SYNC: //20 +# ifdef BLE_GAP_EVENT_PERIODIC_SYNC // IDF 4.0 does not support these + case BLE_GAP_EVENT_PERIODIC_SYNC: // 20 return "BLE_GAP_EVENT_PERIODIC_SYNC"; - - case BLE_GAP_EVENT_PERIODIC_REPORT: //21 + case BLE_GAP_EVENT_PERIODIC_REPORT: // 21 return "BLE_GAP_EVENT_PERIODIC_REPORT"; - - case BLE_GAP_EVENT_PERIODIC_SYNC_LOST: //22 + case BLE_GAP_EVENT_PERIODIC_SYNC_LOST: // 22 return "BLE_GAP_EVENT_PERIODIC_SYNC_LOST"; - - case BLE_GAP_EVENT_SCAN_REQ_RCVD: //23 + case BLE_GAP_EVENT_SCAN_REQ_RCVD: // 23 return "BLE_GAP_EVENT_SCAN_REQ_RCVD"; -#endif + case BLE_GAP_EVENT_PERIODIC_TRANSFER: // 24 + return "BLE_GAP_EVENT_PERIODIC_TRANSFER"; + case BLE_GAP_EVENT_PATHLOSS_THRESHOLD: // 25 + return "BLE_GAP_EVENT_PATHLOSS_THRESHOLD"; + case BLE_GAP_EVENT_TRANSMIT_POWER: // 26 + return "BLE_GAP_EVENT_TRANSMIT_POWER"; + case BLE_GAP_EVENT_PARING_COMPLETE: // 27 + return "BLE_GAP_EVENT_PARING_COMPLETE"; + case BLE_GAP_EVENT_SUBRATE_CHANGE: // 28 + return "BLE_GAP_EVENT_SUBRATE_CHANGE"; + case BLE_GAP_EVENT_VS_HCI: // 29 + return "BLE_GAP_EVENT_VS_HCI"; + case BLE_GAP_EVENT_REATTEMPT_COUNT: // 31 + return "BLE_GAP_EVENT_REATTEMPT_COUNT"; + case BLE_GAP_EVENT_AUTHORIZE: // 32 + return "BLE_GAP_EVENT_AUTHORIZE"; + case BLE_GAP_EVENT_TEST_UPDATE: // 33 + return "BLE_GAP_EVENT_TEST_UPDATE"; +# ifdef BLE_GAP_EVENT_DATA_LEN_CHG + case BLE_GAP_EVENT_DATA_LEN_CHG: // 34 + return "BLE_GAP_EVENT_DATA_LEN_CHG"; +# endif +# ifdef BLE_GAP_EVENT_LINK_ESTAB + case BLE_GAP_EVENT_LINK_ESTAB: // 38 + return "BLE_GAP_EVENT_LINK_ESTAB"; +# endif +# endif default: - NIMBLE_LOGD(LOG_TAG, "gapEventToString: Unknown event type %d 0x%.2x", eventType, eventType); + NIMBLE_LOGD(LOG_TAG, "Unknown event type %d 0x%.2x", eventType, eventType); return "Unknown event type"; } -#else // #if defined(CONFIG_NIMBLE_CPP_ENABLE_GAP_EVENT_CODE_TEXT) +# else // #if defined(CONFIG_NIMBLE_CPP_ENABLE_GAP_EVENT_CODE_TEXT) (void)eventType; return ""; -#endif // #if defined(CONFIG_NIMBLE_CPP_ENABLE_GAP_EVENT_CODE_TEXT) +# endif // #if defined(CONFIG_NIMBLE_CPP_ENABLE_GAP_EVENT_CODE_TEXT) } // gapEventToString -#endif //CONFIG_BT_ENABLED +/** + * @brief Create a hexadecimal string representation of the input data. + * @param [in] source The start of the binary data. + * @param [in] length The length of the data to convert. + * @return A string representation of the data. + */ +std::string NimBLEUtils::dataToHexString(const uint8_t* source, uint8_t length) { + constexpr char hexmap[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; + std::string str{}; + str.resize(length << 1); + + for (uint8_t i = 0; i < length; i++) { + str[2 * i] = hexmap[(source[i] & 0xF0) >> 4]; + str[2 * i + 1] = hexmap[source[i] & 0x0F]; + } + + return str; +} // dataToHexString + +/** + * @brief Generate a random BLE address. + * @param [in] nrpa True to generate a non-resolvable private address, + * false to generate a random static address + * @return The generated address or a NULL address if there was an error. + */ +NimBLEAddress NimBLEUtils::generateAddr(bool nrpa) { + ble_addr_t addr{}; + int rc = ble_hs_id_gen_rnd(nrpa, &addr); + if (rc != 0) { + NIMBLE_LOGE(LOG_TAG, "Generate address failed, rc=%d", rc); + } + + return NimBLEAddress{addr}; +} // generateAddr + +#endif // CONFIG_BT_ENABLED diff --git a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEUtils.h b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEUtils.h index 57d22a0aa..e70cf8fec 100644 --- a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEUtils.h +++ b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEUtils.h @@ -1,50 +1,60 @@ /* - * NimBLEUtils.h + * Copyright 2020-2025 Ryan Powell and + * esp-nimble-cpp, NimBLE-Arduino contributors. * - * Created: on Jan 25 2020 - * Author H2zero + * 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 COMPONENTS_NIMBLEUTILS_H_ -#define COMPONENTS_NIMBLEUTILS_H_ +#ifndef NIMBLE_CPP_UTILS_H_ +#define NIMBLE_CPP_UTILS_H_ #include "nimconfig.h" -#if defined(CONFIG_BT_ENABLED) +#if CONFIG_BT_ENABLED -#if defined(CONFIG_NIMBLE_CPP_IDF) -#include "host/ble_gap.h" -#else -#include "nimble/nimble/host/include/host/ble_gap.h" -#endif +# include -/**** FIX COMPILATION ****/ -#undef min -#undef max -/**************************/ +class NimBLEAddress; -#include - -typedef struct { - void *pATT; - TaskHandle_t task; - int rc; - void *buf; -} ble_task_data_t; +/** + * @brief A structure to hold data for a task that is waiting for a response. + * @details This structure is used in conjunction with NimBLEUtils::taskWait() and NimBLEUtils::taskRelease(). + * All items are optional, the m_pHandle will be set in taskWait(). + */ +struct NimBLETaskData { + NimBLETaskData(void* pInstance = nullptr, int flags = 0, void* buf = nullptr); + ~NimBLETaskData(); + void* m_pInstance{nullptr}; + mutable int m_flags{0}; + void* m_pBuf{nullptr}; + private: + mutable void* m_pHandle{nullptr}; // semaphore or task handle + friend class NimBLEUtils; +}; /** * @brief A BLE Utility class with methods for debugging and general purpose use. */ class NimBLEUtils { -public: - static void dumpGapEvent(ble_gap_event *event, void *arg); - static const char* gapEventToString(uint8_t eventType); - static char* buildHexData(uint8_t* target, const uint8_t* source, uint8_t length); - static const char* advTypeToString(uint8_t advType); - static const char* returnCodeToString(int rc); + public: + static const char* gapEventToString(uint8_t eventType); + static std::string dataToHexString(const uint8_t* source, uint8_t length); + static const char* advTypeToString(uint8_t advType); + static const char* returnCodeToString(int rc); + static NimBLEAddress generateAddr(bool nrpa); + static bool taskWait(const NimBLETaskData& taskData, uint32_t timeout); + static void taskRelease(const NimBLETaskData& taskData, int rc = 0); }; - #endif // CONFIG_BT_ENABLED -#endif // COMPONENTS_NIMBLEUTILS_H_ +#endif // NIMBLE_CPP_UTILS_H_ diff --git a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEValueAttribute.h b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEValueAttribute.h new file mode 100644 index 000000000..a03535a37 --- /dev/null +++ b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEValueAttribute.h @@ -0,0 +1,86 @@ +/* + * Copyright 2020-2025 Ryan Powell 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_VALUE_ATTRIBUTE_H_ +#define NIMBLE_CPP_VALUE_ATTRIBUTE_H_ + +#include "nimconfig.h" +#if CONFIG_BT_ENABLED && (CONFIG_BT_NIMBLE_ROLE_PERIPHERAL || CONFIG_BT_NIMBLE_ROLE_CENTRAL) + +# include "NimBLEAttribute.h" +# include "NimBLEAttValue.h" + +class NimBLEValueAttribute { + public: + NimBLEValueAttribute(uint16_t maxLen = BLE_ATT_ATTR_MAX_LEN, uint16_t initLen = CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH) + : m_value(initLen, maxLen) {} + + /** + * @brief Get a copy of the value of the attribute value. + * @param [in] timestamp (Optional) A pointer to a time_t struct to get the time the value set. + * @return A copy of the attribute value. + */ + NimBLEAttValue getValue(time_t* timestamp) const { return m_value.getValue(timestamp); } + + /** + * @brief Get a copy of the value of the attribute value. + * @return A copy of the attribute value. + */ + NimBLEAttValue getValue() const { return m_value; } + + /** + * @brief Get the length of the attribute value. + * @return The length of the attribute value. + */ + size_t getLength() const { return m_value.size(); } + + /** + * @brief Template to convert the data to . + * @tparam T The type to convert the data to. + * @param [in] timestamp (Optional) A pointer to a time_t struct to get the time the value set. + * @param [in] skipSizeCheck (Optional) If true it will skip checking if the data size is less than sizeof(). + * @return The data converted to or NULL if skipSizeCheck is false and the data is less than sizeof(). + * @details Use: getValue(×tamp, skipSizeCheck); + * Used for types that are trivially copyable and convertible to NimBLEAttValue. + */ + template + typename std::enable_if::value, T>::type + getValue(time_t* timestamp = nullptr, bool skipSizeCheck = false) const { + return m_value.getValue(timestamp, skipSizeCheck); + } + + /** + * @brief Template to convert the data to . + * @tparam T The type to convert the data to. + * @param [in] timestamp (Optional) A pointer to a time_t struct to get the time the value set. + * @param [in] skipSizeCheck (Optional) If true it will skip checking if the data size is less than sizeof(). + * @return The data converted to or NULL if skipSizeCheck is false and the data is less than sizeof(). + * @details Use: getValue(×tamp, skipSizeCheck); + * Used for types that are not trivially copyable and convertible to NimBLEAttValue via it's operators. + */ + template + typename std::enable_if::value && std::is_convertible::value, T>::type + getValue(time_t* timestamp = nullptr, bool skipSizeCheck = false) const { + return m_value; + } + + protected: + NimBLEAttValue m_value{}; +}; + +#endif // CONFIG_BT_ENABLED && (CONFIG_BT_NIMBLE_ROLE_PERIPHERAL || CONFIG_BT_NIMBLE_ROLE_CENTRAL) +#endif // NIMBLE_CPP_ATTRIBUTE_H_ diff --git a/lib/libesp32_div/esp-nimble-cpp/src/nimconfig_rename.h b/lib/libesp32_div/esp-nimble-cpp/src/nimconfig_rename.h index c45aa8bf6..24e050603 100644 --- a/lib/libesp32_div/esp-nimble-cpp/src/nimconfig_rename.h +++ b/lib/libesp32_div/esp-nimble-cpp/src/nimconfig_rename.h @@ -8,19 +8,19 @@ #define CONFIG_BT_NIMBLE_ENABLED #endif -#if defined(CONFIG_NIMBLE_ROLE_OBSERVER) && !defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER) +#if defined(CONFIG_NIMBLE_ROLE_OBSERVER) && !CONFIG_BT_NIMBLE_ROLE_OBSERVER #define CONFIG_BT_NIMBLE_ROLE_OBSERVER #endif -#if defined(CONFIG_NIMBLE_ROLE_BROADCASTER) && !defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER) +#if defined(CONFIG_NIMBLE_ROLE_BROADCASTER) && !CONFIG_BT_NIMBLE_ROLE_BROADCASTER #define CONFIG_BT_NIMBLE_ROLE_BROADCASTER #endif -#if defined(CONFIG_NIMBLE_ROLE_CENTRAL) && !defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL) +#if defined(CONFIG_NIMBLE_ROLE_CENTRAL) && !CONFIG_BT_NIMBLE_ROLE_CENTRAL #define CONFIG_BT_NIMBLE_ROLE_CENTRAL #endif -#if defined(CONFIG_NIMBLE_ROLE_PERIPHERAL) && !defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) +#if defined(CONFIG_NIMBLE_ROLE_PERIPHERAL) && !CONFIG_BT_NIMBLE_ROLE_PERIPHERAL #define CONFIG_BT_NIMBLE_ROLE_PERIPHERAL #endif @@ -59,3 +59,35 @@ #if defined(CONFIG_NIMBLE_MAX_CONNECTIONS ) && !defined(CONFIG_BT_NIMBLE_MAX_CONNECTIONS) #define CONFIG_BT_NIMBLE_MAX_CONNECTIONS CONFIG_NIMBLE_MAX_CONNECTIONS #endif + +#if defined(CONFIG_BT_NIMBLE_EXT_ADV_MAX_SIZE) && !defined(CONFIG_BT_NIMBLE_MAX_EXT_ADV_DATA_LEN) +#define CONFIG_BT_NIMBLE_MAX_EXT_ADV_DATA_LEN CONFIG_BT_NIMBLE_EXT_ADV_MAX_SIZE +#endif + +#if !defined(CONFIG_BTDM_BLE_SCAN_DUPL) && defined(CONFIG_BT_CTRL_BLE_SCAN_DUPL) +#define CONFIG_BTDM_BLE_SCAN_DUPL CONFIG_BT_CTRL_BLE_SCAN_DUPL +#endif + +#if !defined(CONFIG_BTDM_SCAN_DUPL_TYPE_DEVICE) && defined(CONFIG_BT_CTRL_SCAN_DUPL_TYPE_DEVICE) +#define CONFIG_BTDM_SCAN_DUPL_TYPE_DEVICE CONFIG_BT_CTRL_SCAN_DUPL_TYPE_DEVICE +#endif + +#if !defined(CONFIG_BTDM_SCAN_DUPL_TYPE_DATA) && defined(CONFIG_BT_CTRL_SCAN_DUPL_TYPE_DATA) +#define CONFIG_BTDM_SCAN_DUPL_TYPE_DATA CONFIG_BT_CTRL_SCAN_DUPL_TYPE_DATA +#endif + +#if !defined(CONFIG_BTDM_SCAN_DUPL_TYPE_DATA_DEVICE) && defined(CONFIG_BT_CTRL_SCAN_DUPL_TYPE_DATA_DEVICE) +#define CONFIG_BTDM_SCAN_DUPL_TYPE_DATA_DEVICE CONFIG_BT_CTRL_SCAN_DUPL_TYPE_DATA_DEVICE +#endif + +#ifdef CONFIG_BT_LE_LL_CFG_FEAT_LE_CODED_PHY +#define CONFIG_BT_NIMBLE_LL_CFG_FEAT_LE_CODED_PHY CONFIG_BT_LE_LL_CFG_FEAT_LE_CODED_PHY +#endif + +#ifdef CONFIG_BT_LE_LL_CFG_FEAT_LE_2M_PHY +#define CONFIG_BT_NIMBLE_LL_CFG_FEAT_LE_2M_PHY CONFIG_BT_LE_LL_CFG_FEAT_LE_2M_PHY +#endif + +#ifdef CONFIG_BT_LE_50_FEATURE_SUPPORT +#define CONFIG_BT_NIMBLE_50_FEATURE_SUPPORT CONFIG_BT_LE_50_FEATURE_SUPPORT +#endif diff --git a/tasmota/include/xsns_62_esp32_mi.h b/tasmota/include/xsns_62_esp32_mi.h index 4b8f6ea87..b06eff419 100644 --- a/tasmota/include/xsns_62_esp32_mi.h +++ b/tasmota/include/xsns_62_esp32_mi.h @@ -189,6 +189,7 @@ struct { TaskHandle_t ConnTask = nullptr; TaskHandle_t ServerTask = nullptr; MI32connectionContextBerry_t *conCtx = nullptr; + uint16_t connID; union { struct { uint32_t init:1; diff --git a/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_MI32.ino b/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_MI32.ino index 2f506828a..5550ea99e 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_MI32.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_MI32.ino @@ -104,8 +104,8 @@ extern "C" { extern bool MI32setBerryCtxSvc(const char *Svc, bbool discoverAttributes); extern bool MI32setBerryCtxChr(const char *Chr); extern bool MI32setBerryCtxMAC(uint8_t *MAC, uint8_t type); - extern bool MI32addMACtoBlockList(uint8_t *MAC, uint8_t type); extern bool MI32addMACtoWatchList(uint8_t *MAC, uint8_t type); + extern void MI32setBerryStoreRec(uint8_t *buffer, size_t size); int be_BLE_init(bvm *vm); int be_BLE_init(bvm *vm) { @@ -120,6 +120,10 @@ extern "C" { MI32BerryLoop(); } + void be_BLE_store(uint8_t *buf, size_t size){ + MI32setBerryStoreRec(buf, size); + } + void be_BLE_reg_conn_cb(void* function, uint8_t *buffer); void be_BLE_reg_conn_cb(void* function, uint8_t *buffer){ MI32setBerryConnCB(function,buffer); @@ -198,20 +202,6 @@ extern "C" { be_raisef(vm, "ble_error", "BLE: could not run operation"); } - void be_BLE_adv_block(struct bvm *vm, uint8_t *buf, size_t size, uint8_t type); - void be_BLE_adv_block(struct bvm *vm, uint8_t *buf, size_t size, uint8_t type){ - if(!be_BLE_MAC_size(vm, size)){ - return; - } - uint8_t _type = 0; - if(type){ - _type = type; - } - if(MI32addMACtoBlockList(buf, _type)) return; - - be_raisef(vm, "ble_error", "BLE: could not block MAC"); - } - void be_BLE_adv_watch(struct bvm *vm, uint8_t *buf, size_t size, uint8_t type); void be_BLE_adv_watch(struct bvm *vm, uint8_t *buf, size_t size, uint8_t type){ if(!be_BLE_MAC_size(vm, size)){ @@ -232,7 +222,7 @@ extern "C" { if(!device){ return NimBLEDevice::getServer()->getPeerInfo(0); } else { - return NimBLEDevice::getClientList()->front()->getConnInfo(); + return device->getConnInfo(); } } @@ -280,7 +270,7 @@ extern "C" { if(MI32.mode.connected == 1 || MI32.ServerTask != nullptr){ NimBLEClient* _device = nullptr; if(MI32.mode.connected == 1){ - _device = NimBLEDevice::getClientList()->front(); + _device = NimBLEDevice::getClientByHandle(MI32.connID); } NimBLEConnInfo _info = be_BLE_get_ConnInfo(_device); @@ -295,7 +285,17 @@ extern "C" { be_map_insert_bool(vm, "master", _info.isMaster()); be_map_insert_bool(vm, "encrypted", _info.isEncrypted()); be_map_insert_bool(vm, "authenticated", _info.isAuthenticated()); - if(_device == nullptr) be_map_insert_str(vm, "name", NimBLEDevice::getServer()->getPeerName(_info).c_str()); // ESP32 is server + if(_device == nullptr) { + auto _remote_client = NimBLEDevice::getServer()->getClient(_info); + if(_remote_client != nullptr){ + auto _name = _remote_client->getValue(NimBLEUUID((uint16_t)0x1800), NimBLEUUID((uint16_t)0x2A00)); //GAP, name + if(_name){ + be_map_insert_str(vm, "name", _name.c_str()); // ESP32 is server + } + } else { + be_map_insert_str(vm, "name", ""); + } + } ble_store_value_sec value_sec; ble_sm_read_bond(_info.getConnHandle(), &value_sec); @@ -368,7 +368,6 @@ BLE.conn_cb(cb,buffer) BLE.adv_cb(cb,buffer) BLE.serv_cb(cb,buffer) BLE.adv_watch(MAC) -BLE.adv_block(MAC) MI32.devices() MI32.get_name(slot) diff --git a/tasmota/tasmota_xdrv_driver/xdrv_79_esp32_ble.ino b/tasmota/tasmota_xdrv_driver/xdrv_79_esp32_ble.ino index 4fd0c8211..99245db41 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_79_esp32_ble.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_79_esp32_ble.ino @@ -154,7 +154,7 @@ i.e. the Bluetooth of the ESP can be shared without conflict. #include #include -#include "NimBLEEddystoneURL.h" +// #include "NimBLEEddystoneURL.h" #include "NimBLEEddystoneTLM.h" #include "NimBLEBeacon.h" @@ -254,7 +254,7 @@ struct generic_sensor_t { //////////////////////////////////////////////////////////////// // structure for callbacks from other drivers from advertisements. struct ble_advertisment_t { - BLEAdvertisedDevice *advertisedDevice; // the full NimBLE advertisment, in case people need MORE info. + const BLEAdvertisedDevice *advertisedDevice; // the full NimBLE advertisment, in case people need MORE info. uint32_t totalCount; uint8_t addr[6]; @@ -1232,10 +1232,10 @@ void setDetails(ble_advertisment_t *ad){ maxlen -= len; } - BLEAdvertisedDevice *advertisedDevice = ad->advertisedDevice; + const BLEAdvertisedDevice *advertisedDevice = ad->advertisedDevice; - uint8_t* payload = advertisedDevice->getPayload(); - size_t payloadlen = advertisedDevice->getPayloadLength(); + const uint8_t* payload = advertisedDevice->getPayload().data(); + size_t payloadlen = advertisedDevice->getPayload().size(); if (payloadlen && (maxlen > 30)){ // will truncate if not enough space strcpy(p, ",\"p\":\""); p += 6; @@ -1367,11 +1367,11 @@ static BLESensorCallback clientCB; class BLEAdvCallbacks: public NimBLEScanCallbacks { - void onScanEnd(NimBLEScanResults results) { + void onScanEnd(const NimBLEScanResults results) { BLEscanEndedCB(results); } - void onResult(NimBLEAdvertisedDevice* advertisedDevice) { + void onResult(const NimBLEAdvertisedDevice* advertisedDevice) { TasAutoMutex localmutex(&BLEOperationsRecursiveMutex, "BLEAddCB"); uint64_t now = esp_timer_get_time(); BLEScanLastAdvertismentAt = now; // note the time of the last advertisment @@ -1388,7 +1388,7 @@ class BLEAdvCallbacks: public NimBLEScanCallbacks { BLEAdvertisment.addrtype = address.getType(); - memcpy(BLEAdvertisment.addr, address.getNative(), 6); + memcpy(BLEAdvertisment.addr, address.getVal(), 6); ReverseMAC(BLEAdvertisment.addr); BLEAdvertisment.RSSI = RSSI; @@ -1529,7 +1529,7 @@ static void BLEGenNotifyCB(NimBLERemoteCharacteristic* pRemoteCharacteristic, ui if (BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG,PSTR("BLE: Notified length: %u"),length); #endif // find the operation this is associated with - NimBLERemoteService *pSvc = pRemoteCharacteristic->getRemoteService(); + const NimBLERemoteService *pSvc = pRemoteCharacteristic->getRemoteService(); if (!pSvc){ #ifdef BLE_ESP32_DEBUG @@ -1911,7 +1911,7 @@ static void BLETaskRunCurrentOperation(BLE_ESP32::generic_sensor_t** pCurrentOpe op->state = GEN_STATE_STARTED; char addrstr[13]; - const uint8_t* m_address = op->addr.getNative(); + const uint8_t* m_address = op->addr.getVal(); snprintf(addrstr, sizeof(addrstr), "%02X%02X%02X%02X%02X%02X", m_address[5], m_address[4], m_address[3], m_address[2], m_address[1], m_address[0]); #ifdef BLE_ESP32_DEBUG @@ -2169,7 +2169,7 @@ static void BLETaskRunTaskDoneOperation(BLE_ESP32::generic_sensor_t** op, NimBLE } waits++; if (waits == 5){ - int conn_id = (*ppClient)->getConnId(); + int conn_id = (*ppClient)->getConnHandle(); #ifdef DEPENDSONNIMBLEARDUINO ble_gap_conn_broken(conn_id, -1); #endif @@ -3516,7 +3516,7 @@ std::string BLETriggerResponse(generic_sensor_t *toSend){ if (toSend->addr != NimBLEAddress()){ out = out + "\",\"MAC\":\""; uint8_t addrrev[6]; - memcpy(addrrev, toSend->addr.getNative(), 6); + memcpy(addrrev, toSend->addr.getVal(), 6); ReverseMAC(addrrev); dump(temp, 13, addrrev, 6); out = out + temp; diff --git a/tasmota/tasmota_xdrv_driver/xdrv_85_esp32_ble_eq3_trv.ino b/tasmota/tasmota_xdrv_driver/xdrv_85_esp32_ble_eq3_trv.ino index 81d455b04..982aa2480 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_85_esp32_ble_eq3_trv.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_85_esp32_ble_eq3_trv.ino @@ -324,7 +324,7 @@ bool EQ3Operation(const uint8_t *MAC, const uint8_t *data, int datalen, int cmdt #endif } - NimBLEAddress addr((uint8_t *)MAC); + NimBLEAddress addr((uint8_t *)MAC,0); //type 0 is public op->addr = addr; bool havechar = false; @@ -397,7 +397,7 @@ int EQ3ParseOp(BLE_ESP32::generic_sensor_t *op, bool success, int retries){ ResponseClear(); uint8_t addrev[7]; - const uint8_t *native = op->addr.getNative(); + const uint8_t *native = op->addr.getVal(); memcpy(addrev, native, 6); BLE_ESP32::ReverseMAC(addrev); @@ -594,7 +594,7 @@ int EQ3GenericOpCompleteFn(BLE_ESP32::generic_sensor_t *op){ if (op->state <= GEN_STATE_FAILED){ uint8_t addrev[7]; - const uint8_t *native = op->addr.getNative(); + const uint8_t *native = op->addr.getVal(); memcpy(addrev, native, 6); BLE_ESP32::ReverseMAC(addrev); @@ -804,7 +804,7 @@ const char *EQ3Names[] = { int TaskEQ3advertismentCallback(BLE_ESP32::ble_advertisment_t *pStruct) { // we will try not to use this... - BLEAdvertisedDevice *advertisedDevice = pStruct->advertisedDevice; + const BLEAdvertisedDevice *advertisedDevice = pStruct->advertisedDevice; std::string sname = advertisedDevice->getName(); @@ -845,8 +845,8 @@ int TaskEQ3advertismentCallback(BLE_ESP32::ble_advertisment_t *pStruct) if (BLE_ESP32::BLEDebugMode) AddLog(LOG_LEVEL_DEBUG, PSTR("EQ3: %s: saw device"),advertisedDevice->getAddress().toString().c_str()); #endif - uint8_t* payload = advertisedDevice->getPayload(); - size_t payloadlen = advertisedDevice->getPayloadLength(); + uint8_t* payload = (uint8_t *)advertisedDevice->getPayload().data(); + size_t payloadlen = advertisedDevice->getPayload().size(); char name[20] = {0}; char serial[20] = {0}; diff --git a/tasmota/tasmota_xsns_sensor/xsns_52_esp32_ibeacon_ble.ino b/tasmota/tasmota_xsns_sensor/xsns_52_esp32_ibeacon_ble.ino index 2e886af91..201aee852 100644 --- a/tasmota/tasmota_xsns_sensor/xsns_52_esp32_ibeacon_ble.ino +++ b/tasmota/tasmota_xsns_sensor/xsns_52_esp32_ibeacon_ble.ino @@ -202,7 +202,7 @@ int advertismentCallback(BLE_ESP32::ble_advertisment_t *pStruct) struct IBEACON ib; if (!iBeaconEnable) return 0; - BLEAdvertisedDevice *advertisedDevice = pStruct->advertisedDevice; + const BLEAdvertisedDevice *advertisedDevice = pStruct->advertisedDevice; char sRSSI[6]; itoa(pStruct->RSSI,sRSSI,10); @@ -237,9 +237,9 @@ int advertismentCallback(BLE_ESP32::ble_advertisment_t *pStruct) manufacturerData[1] == 0x00) { BLEBeacon oBeacon = BLEBeacon(); - oBeacon.setData(std::string((char *)manufacturerData, manufacturerDataLen)); + oBeacon.setData(manufacturerData, manufacturerDataLen); uint8_t UUID[16]; - memcpy(UUID,oBeacon.getProximityUUID().getNative()->u128.value,16); + memcpy(UUID,oBeacon.getProximityUUID().getValue(),16); //TODO: check correct size ESP32BLE_ReverseStr(UUID,16); // uint16_t Major = ENDIAN_CHANGE_U16(oBeacon.getMajor()); diff --git a/tasmota/tasmota_xsns_sensor/xsns_62_esp32_mi.ino b/tasmota/tasmota_xsns_sensor/xsns_62_esp32_mi.ino index 6abd6b470..b98228043 100644 --- a/tasmota/tasmota_xsns_sensor/xsns_62_esp32_mi.ino +++ b/tasmota/tasmota_xsns_sensor/xsns_62_esp32_mi.ino @@ -70,7 +70,7 @@ void MI32notifyCB(NimBLERemoteCharacteristic* pRemoteCharacteristic, uint8_t* pData, size_t length, bool isNotify); void MI32AddKey(mi_bindKey_t keyMAC); -void MI32HandleEveryDevice(NimBLEAdvertisedDevice* advertisedDevice, uint8_t addr[6], int RSSI); +void MI32HandleEveryDevice(const NimBLEAdvertisedDevice* advertisedDevice, uint8_t addr[6], int RSSI); std::vector MIBLEsensors; RingbufHandle_t BLERingBufferQueue = nullptr; @@ -88,6 +88,7 @@ class MI32SensorCallback : public NimBLEClientCallbacks { MI32.infoMsg = MI32_DID_CONNECT; MI32.mode.willConnect = 0; MI32.mode.connected = 1; + MI32.connID = pclient->getConnHandle(); pclient->updateConnParams(8,16,0,1000); } void onDisconnect(NimBLEClient* pclient, int reason) { @@ -101,20 +102,20 @@ class MI32SensorCallback : public NimBLEClientCallbacks { }; class MI32AdvCallbacks: public NimBLEScanCallbacks { - void onScanEnd(NimBLEScanResults results) { + void onScanEnd(const NimBLEScanResults &results, int reason) { MI32.infoMsg = MI32_SCAN_ENDED; MI32.mode.runningScan = 0; MI32.mode.deleteScanTask = 1; // if scan ended dew to a BLE controller error, make sure we stop the task } - void IRAM_ATTR onResult(NimBLEAdvertisedDevice* advertisedDevice) { + void IRAM_ATTR onResult(const NimBLEAdvertisedDevice* advertisedDevice) { static bool _mutex = false; if(_mutex) return; _mutex = true; int RSSI = advertisedDevice->getRSSI(); uint8_t addr[6]; - memcpy(addr,advertisedDevice->getAddress().getNative(),6); + memcpy(addr,advertisedDevice->getAddress().getVal(),6); MI32_ReverseMAC(addr); size_t ServiceDataLength = 0; @@ -123,8 +124,8 @@ class MI32AdvCallbacks: public NimBLEScanCallbacks { memcpy(_packet->MAC,addr,6); _packet->addressType = advertisedDevice->getAddressType(); _packet->RSSI = (uint8_t)RSSI; - uint8_t *_payload = advertisedDevice->getPayload(); - _packet->length = advertisedDevice->getPayloadLength(); + const uint8_t *_payload = advertisedDevice->getPayload().data(); + _packet->length = advertisedDevice->getPayload().size(); memcpy(_packet->payload,_payload, _packet->length); MI32.mode.triggerBerryAdvCB = 1; } @@ -137,7 +138,7 @@ class MI32AdvCallbacks: public NimBLEScanCallbacks { return; } - uint16_t UUID = advertisedDevice->getServiceDataUUID(0).getNative()->u16.value; + uint16_t UUID = *(uint16_t*)advertisedDevice->getServiceDataUUID(0).getValue(); ServiceDataLength = advertisedDevice->getServiceData(0).length(); if(UUID==0xfe95) { @@ -168,7 +169,7 @@ class MI32ServerCallbacks: public NimBLEServerCallbacks { } item; item.header.length = 6; item.header.type = BLE_OP_ON_CONNECT; - memcpy(item.buffer,connInfo.getAddress().getNative(),6); + memcpy(item.buffer,connInfo.getAddress().getVal(),6); xRingbufferSend(BLERingBufferQueue, (const void*)&item, sizeof(BLERingBufferItem_t) + 6 , pdMS_TO_TICKS(1)); MI32.infoMsg = MI32_SERV_CLIENT_CONNECTED; }; @@ -187,7 +188,7 @@ class MI32ServerCallbacks: public NimBLEServerCallbacks { NimBLEDevice::startAdvertising(); #endif }; - void onAuthenticationComplete(const NimBLEConnInfo& connInfo) { + void onAuthenticationComplete(NimBLEConnInfo& connInfo) { struct{ BLERingBufferItem_t header; uint8_t buffer[sizeof(ble_store_value_sec)]; @@ -203,13 +204,13 @@ class MI32ServerCallbacks: public NimBLEServerCallbacks { }; class MI32CharacteristicCallbacks: public NimBLECharacteristicCallbacks { - void onRead(NimBLECharacteristic* pCharacteristic, NimBLEConnInfo& connInfo){ + void onRead(NimBLECharacteristic* pCharacteristic, NimBLEConnInfo& connInfo) { struct{ BLERingBufferItem_t header; } item; item.header.length = 0; item.header.type = BLE_OP_ON_READ; - item.header.returnCharUUID = pCharacteristic->getUUID().getNative()->u16.value; + item.header.returnCharUUID = *(uint16_t*)pCharacteristic->getUUID().getValue(); item.header.handle = pCharacteristic->getHandle(); xRingbufferSend(BLERingBufferQueue, (const void*)&item, sizeof(BLERingBufferItem_t), pdMS_TO_TICKS(1)); }; @@ -219,11 +220,11 @@ class MI32CharacteristicCallbacks: public NimBLECharacteristicCallbacks { BLERingBufferItem_t header; uint8_t buffer[255]; } item; - item.header.length = pCharacteristic->getDataLength();; + item.header.length = pCharacteristic->getValue().size(); item.header.type = BLE_OP_ON_WRITE; - item.header.returnCharUUID = pCharacteristic->getUUID().getNative()->u16.value; + item.header.returnCharUUID = *(uint16_t*)pCharacteristic->getUUID().getValue(); item.header.handle = pCharacteristic->getHandle(); - memcpy(item.buffer,pCharacteristic->getValue(),pCharacteristic->getDataLength()); + memcpy(item.buffer,pCharacteristic->getValue().data(),pCharacteristic->getValue().size()); xRingbufferSend(BLERingBufferQueue, (const void*)&item, sizeof(BLERingBufferItem_t) + item.header.length , pdMS_TO_TICKS(1)); }; @@ -237,7 +238,7 @@ class MI32CharacteristicCallbacks: public NimBLECharacteristicCallbacks { } item; item.header.length = 4; item.header.type = BLE_OP_ON_STATUS; - item.header.returnCharUUID = pCharacteristic->getUUID().getNative()->u16.value; + item.header.returnCharUUID = *(uint16_t*)pCharacteristic->getUUID().getValue(); item.header.handle = pCharacteristic->getHandle(); xRingbufferSend(BLERingBufferQueue, (const void*)&item, sizeof(BLERingBufferItem_t) + 4, pdMS_TO_TICKS(1)); }; @@ -248,7 +249,7 @@ class MI32CharacteristicCallbacks: public NimBLECharacteristicCallbacks { } item; item.header.length = 0; item.header.type = BLE_OP_ON_UNSUBSCRIBE + subValue;; - item.header.returnCharUUID = pCharacteristic->getUUID().getNative()->u16.value; + item.header.returnCharUUID = *(uint16_t*)pCharacteristic->getUUID().getValue(); item.header.handle = pCharacteristic->getHandle(); xRingbufferSend(BLERingBufferQueue, (const void*)&item, sizeof(BLERingBufferItem_t), pdMS_TO_TICKS(1)); }; @@ -264,7 +265,7 @@ void MI32notifyCB(NimBLERemoteCharacteristic* pRemoteCharacteristic, uint8_t* pD item.header.length = length; // item.header.type = 103; does not matter for now memcpy(item.buffer,pData,length); - item.header.returnCharUUID = pRemoteCharacteristic->getUUID().getNative()->u16.value; + item.header.returnCharUUID = *(uint16_t*)pRemoteCharacteristic->getUUID().getValue(); item.header.handle = pRemoteCharacteristic->getHandle(); xRingbufferSend(BLERingBufferQueue, (const void*)&item, sizeof(BLERingBufferItem_t) + length , pdMS_TO_TICKS(5)); MI32.mode.readingDone = 1; @@ -733,6 +734,14 @@ extern "C" { MI32BLELoop(); } + void MI32setBerryStoreRec(uint8_t *buffer, size_t size){ + constexpr size_t sec_size = sizeof(ble_store_value_sec); + if(sec_size == size){ + ble_store_write_peer_sec((const struct ble_store_value_sec*)&buffer); + AddLog(LOG_LEVEL_INFO,PSTR("BLE: write peer")); + } + } + bool MI32runBerryConfig(uint16_t operation){ bool success = false; #ifdef CONFIG_BT_NIMBLE_EXT_ADV @@ -758,7 +767,7 @@ extern "C" { if(MI32.conCtx->buffer[0] == 5){ uint16_t itvl_min = MI32.conCtx->buffer[2] + (MI32.conCtx->buffer[3] << 8); uint16_t itvl_max = MI32.conCtx->buffer[4] + (MI32.conCtx->buffer[5] << 8); - pAdvertising->setAdvertisementType(MI32.conCtx->buffer[1]); + pAdvertising->setConnectableMode(MI32.conCtx->buffer[1]); pAdvertising->setMinInterval(itvl_min); pAdvertising->setMaxInterval(itvl_max); AddLog(LOG_LEVEL_DEBUG,PSTR("BLE: adv params: type: %u, min: %u, max: %u"),MI32.conCtx->buffer[1], (uint16_t)(itvl_min * 0.625), (uint16_t)(itvl_max * 0.625)) ; @@ -867,7 +876,7 @@ extern "C" { if(MI32.conCtx != nullptr){ MI32.conCtx->charUUID = NimBLEUUID(Chr); AddLog(LOG_LEVEL_DEBUG,PSTR("M32: CHR: %s"),MI32.conCtx->charUUID.toString().c_str()); - uint16_t _uuid = MI32.conCtx->charUUID.getNative()->u16.value; //if not "notify op" -> present requested characteristic as return UUID + uint16_t _uuid = *(uint16_t*)MI32.conCtx->charUUID.getValue(); //if not "notify op" -> present requested characteristic as return UUID MI32.conCtx->returnCharUUID = _uuid; AddLog(LOG_LEVEL_DEBUG,PSTR("M32: return 16-bit UUID: %04x"),MI32.conCtx->returnCharUUID); return true; @@ -890,11 +899,6 @@ extern "C" { MI32.beAdvBuf = buffer; } - bool MI32addMACtoBlockList(uint8_t *MAC, uint8_t type){ - NimBLEDevice::addIgnored(NimBLEAddress(MAC,type)); - return NimBLEDevice::isIgnored(NimBLEAddress(MAC,type)); - } - bool MI32addMACtoWatchList(uint8_t *MAC, uint8_t type){ NimBLEAddress _newAddress = NimBLEAddress(MAC,type); if(MI32Scan==nullptr){ @@ -1224,7 +1228,7 @@ bool MI32ConnectActiveSensor(){ // only use inside a task !! } MI32Client = nullptr; - if(NimBLEDevice::getClientListSize()) { + if(NimBLEDevice::getCreatedClientCount() > 0) { MI32Client = NimBLEDevice::getClientByPeerAddress(_address); } if (!MI32Client){ @@ -1247,17 +1251,18 @@ bool MI32ConnectActiveSensor(){ // only use inside a task !! * ... next service */ void MI32ConnectionGetServices(){ - std::vector *srvvector = MI32Client->getServices(true); // refresh - MI32.conCtx->buffer[1] = srvvector->size(); // number of services + std::vector srvvector = MI32Client->getServices(true); // refresh + MI32.conCtx->buffer[1] = srvvector.size(); // number of services uint32_t i = 2; - for (auto &srv: *srvvector) { + for (auto &srv: srvvector) { MI32.conCtx->buffer[i] = srv->getUUID().bitSize(); // 16/128 bit if(MI32.conCtx->buffer[i] == 16){ - MI32.conCtx->buffer[i+1] = srv->getUUID().getNative()->u16.value & 0xff; - MI32.conCtx->buffer[i+2] = srv->getUUID().getNative()->u16.value >> 8; + const uint16_t _uuid16 = *(uint16_t*)srv->getUUID().getValue(); + MI32.conCtx->buffer[i+1] = _uuid16 & 0xff; + MI32.conCtx->buffer[i+2] = _uuid16 >> 8; } else{ - memcpy((MI32.conCtx->buffer)+i+1,srv->getUUID().getNative()->u128.value,MI32.conCtx->buffer[i]); // the UUID + memcpy((MI32.conCtx->buffer)+i+1,srv->getUUID().getValue(),MI32.conCtx->buffer[i]); // the UUID } i += 1 + (MI32.conCtx->buffer[i]/8); } @@ -1276,17 +1281,17 @@ void MI32ConnectionGetServices(){ */ void MI32ConnectionGetCharacteristics(NimBLERemoteService* pSvc); void MI32ConnectionGetCharacteristics(NimBLERemoteService* pSvc){ - std::vector *charvector = pSvc->getCharacteristics(true); // refresh - MI32.conCtx->buffer[1] = charvector->size(); // number of characteristics + auto charvector = pSvc->getCharacteristics(); // refresh + MI32.conCtx->buffer[1] = charvector.size(); // number of characteristics uint32_t i = 2; - for (auto &chr: *charvector) { + for (auto &chr: charvector) { MI32.conCtx->buffer[i] = chr->getUUID().bitSize(); // 16/128 bit if(MI32.conCtx->buffer[i] == 16){ - MI32.conCtx->buffer[i+1] = chr->getUUID().getNative()->u16.value & 0xff; - MI32.conCtx->buffer[i+2] = chr->getUUID().getNative()->u16.value >> 8; + MI32.conCtx->buffer[i+1] = *(uint16_t*)chr->getUUID().getValue() & 0xff; + MI32.conCtx->buffer[i+2] = *(uint16_t*)chr->getUUID().getValue() >> 8; } else{ - memcpy((MI32.conCtx->buffer)+i+1,chr->getUUID().getNative()->u128.value,MI32.conCtx->buffer[i]); // the UUID + memcpy((MI32.conCtx->buffer)+i+1,chr->getUUID().getValue(),MI32.conCtx->buffer[i]); // the UUID } i += 1 + (MI32.conCtx->buffer[i]/8); MI32.conCtx->buffer[i] = chr->getProperties(); // flags as bitfield @@ -1342,7 +1347,7 @@ void MI32ConnectionTask(void *pvParameters){ } NimBLERemoteService* pSvc = nullptr; NimBLERemoteCharacteristic* pChr = nullptr; - std::vector*charvector = nullptr; + std::vectorcharvector; // AddLog(LOG_LEVEL_INFO,PSTR("M32: start connection loop")); bool keepConnectionAlive = true; @@ -1439,7 +1444,7 @@ void MI32ConnectionTask(void *pvParameters){ else { // characteristic selected by UUID charvector = pSvc->getCharacteristics(true); // always try to subscribe to all characteristics with the same UUID uint32_t position = 1; - for (auto &it: *charvector) { + for (auto &it: charvector) { if (it->getUUID() == MI32.conCtx->charUUID) { if (it->canNotify()) { if(!it->subscribe(true, MI32notifyCB, MI32.conCtx->response)) { @@ -1552,7 +1557,7 @@ void MI32ServerSetAdv(NimBLEServer *pServer, std::vector& servic #ifdef CONFIG_BT_NIMBLE_EXT_ADV //TODO #else - pAdvertising->setAdvertisementType(MI32.conCtx->arg1); + pAdvertising->setConnectableMode(MI32.conCtx->arg1); // AddLog(LOG_LEVEL_DEBUG,PSTR("BLE: AdvertisementType: %u"),MI32.conCtx->arg1); #endif // AddLog(LOG_LEVEL_DEBUG,PSTR("BLE: AdvertisementType: %u"),MI32.conCtx->arg1); @@ -1600,7 +1605,7 @@ void MI32ServerSetAdv(NimBLEServer *pServer, std::vector& servic } #else NimBLEAdvertisementData adv; - adv.addData((char *)&MI32.conCtx->buffer[1], MI32.conCtx->buffer[0]); + adv.addData((const uint8_t*)&MI32.conCtx->buffer[1], MI32.conCtx->buffer[0]); if(MI32.conCtx->operation == BLE_OP_SET_ADV){ pAdvertising->setAdvertisementData(adv); // replace whole advertisement with our custom data from the Berry side if(pAdvertising->isAdvertising() == false && !shallStartServices){ // first advertisement @@ -1610,7 +1615,7 @@ void MI32ServerSetAdv(NimBLEServer *pServer, std::vector& servic } else { pAdvertising->setScanResponseData(adv); - pAdvertising->setScanResponse(true); + pAdvertising->enableScanResponse(true); } #endif //CONFIG_BT_NIMBLE_EXT_ADV @@ -1670,7 +1675,7 @@ void MI32ServerSetCharacteristic(NimBLEServer *pServer, std::vectorgetUUID().getNative()->u16.value; + item.header.returnCharUUID = *(uint16_t*)pCharacteristic->getUUID().getValue(); item.header.handle = pCharacteristic->getHandle(); xRingbufferSend(BLERingBufferQueue, (const void*)&item, sizeof(BLERingBufferItem_t), pdMS_TO_TICKS(1)); } @@ -1678,7 +1683,9 @@ void MI32ServerSetCharacteristic(NimBLEServer *pServer, std::vectorerror = MI32_CONN_NO_ERROR; NimBLEServer *pServer = NimBLEDevice::createServer(); - pServer->setCallbacks(new MI32ServerCallbacks()); + auto _srvCB = new MI32ServerCallbacks(); + pServer->setCallbacks(_srvCB,true); + MI32.mode.readyForNextServerJob = 1; MI32.mode.deleteServerTask = 0; std::vector servicesToStart; @@ -2146,11 +2153,11 @@ uint16_t MI32checkRPA(uint8_t *addr) { return 0xff; } -void MI32HandleEveryDevice(NimBLEAdvertisedDevice* advertisedDevice, uint8_t addr[6], int RSSI) { +void MI32HandleEveryDevice(const NimBLEAdvertisedDevice* advertisedDevice, uint8_t addr[6], int RSSI) { uint16_t _slot; if (advertisedDevice->getAddressType() == BLE_ADDR_PUBLIC) { _slot = MIBLEgetSensorSlot(addr, 0, 0);} - else if (advertisedDevice->getAddress().isRpa() && MI32.mode.IRKinCfg == 1) { _slot = MI32checkRPA(addr);} + else if (advertisedDevice->getAddressType() == BLE_ADDR_RANDOM && MI32.mode.IRKinCfg == 1) { _slot = MI32checkRPA(addr);} else {return;} if(_slot==0xff) { @@ -2168,8 +2175,8 @@ void MI32HandleEveryDevice(NimBLEAdvertisedDevice* advertisedDevice, uint8_t add _sensor.payload = new uint8_t[64](); } if(_sensor.payload != nullptr) { - memcpy(_sensor.payload, advertisedDevice->getPayload(), advertisedDevice->getPayloadLength()); - _sensor.payload_len = advertisedDevice->getPayloadLength(); + memcpy(_sensor.payload, advertisedDevice->getPayload().data(), advertisedDevice->getPayload().size()); + _sensor.payload_len = advertisedDevice->getPayload().size(); bitSet(MI32.widgetSlot,_slot); MI32addHistory(_sensor.temp_history, 0.0f, 3); // reuse temp_history as sighting history _sensor.RSSI=RSSI; @@ -2624,6 +2631,7 @@ void MI32sendWidget(uint32_t slot){ } void MI32InitGUI(void){ + MI32.widgetSlot=0; WSContentStart_P("m32"); WSContentSend_P(HTTP_MI32_SCRIPT_1); WSContentSendStyle(); diff --git a/tasmota/tasmota_xsns_sensor/xsns_62_esp32_mi_ble.ino b/tasmota/tasmota_xsns_sensor/xsns_62_esp32_mi_ble.ino index 7690af927..0ed22f3c1 100644 --- a/tasmota/tasmota_xsns_sensor/xsns_62_esp32_mi_ble.ino +++ b/tasmota/tasmota_xsns_sensor/xsns_62_esp32_mi_ble.ino @@ -677,14 +677,14 @@ bool MI32Operation(int slot, int optype, const char *svc, const char *charactist } if (slot >= 0){ - op->addr = NimBLEAddress(MIBLEsensors[slot].MAC); + op->addr = NimBLEAddress(MIBLEsensors[slot].MAC,0); } else { if (!addr){ AddLog(LOG_LEVEL_ERROR, PSTR("M32: No addr")); BLE_ESP32::freeOperation(&op); return 0; } - op->addr = NimBLEAddress(addr); + op->addr = NimBLEAddress(addr, 0); } bool havechar = false; @@ -992,7 +992,7 @@ int genericOpCompleteFn(BLE_ESP32::generic_sensor_t *op){ uint8_t addrrev[6]; memcpy(addrrev, MIBLEsensors[slot].MAC, 6); //BLE_ESP32::ReverseMAC(addrrev); - NimBLEAddress addr(addrrev); + NimBLEAddress addr(addrrev, 0); bool fail = false; if (op->addr != addr){ @@ -1077,7 +1077,7 @@ int genericOpCompleteFn(BLE_ESP32::generic_sensor_t *op){ int MI32advertismentCallback(BLE_ESP32::ble_advertisment_t *pStruct) { // we will try not to use this... - BLEAdvertisedDevice *advertisedDevice = pStruct->advertisedDevice; + const BLEAdvertisedDevice *advertisedDevice = pStruct->advertisedDevice; // AddLog(LOG_LEVEL_DEBUG, PSTR("M32: Advertised Device: %s Buffer: %u"),advertisedDevice->getAddress().toString().c_str(),advertisedDevice->getServiceData(0).length()); int RSSI = pStruct->RSSI; @@ -1106,12 +1106,12 @@ int MI32advertismentCallback(BLE_ESP32::ble_advertisment_t *pStruct) NimBLEUUID UUIDBig = advertisedDevice->getServiceDataUUID(0);//.getNative()->u16.value; - const ble_uuid_any_t* native = UUIDBig.getNative(); - if (native->u.type != 16){ + const ble_uuid_t* native = UUIDBig.getBase(); + if (native->type != 16){ //not interested in 128 bit; return 0; } - uint16_t UUID = native->u16.value; + uint16_t UUID = *(uint16_t*)UUIDBig.getValue(); if (BLE_ESP32::BLEDebugMode) AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("M32: %s: svc[0] UUID (%x)"), MIaddrStr(addr), UUID); std::string ServiceDataStr = advertisedDevice->getServiceData(0);