mirror of
https://github.com/h2zero/esp-nimble-cpp.git
synced 2026-04-17 23:18:37 +02:00
Compare commits
133 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dd1b884c41 | ||
|
|
d3f5063ace | ||
|
|
2db8fedb77 | ||
|
|
56580cbf51 | ||
|
|
9d6e48caa4 | ||
|
|
83fb1dfef8 | ||
|
|
4cf0e7c705 | ||
|
|
ee325395d5 | ||
|
|
1b6f152ae7 | ||
|
|
ee8ea37ebb | ||
|
|
8c51a9027c | ||
|
|
c87b76e997 | ||
|
|
84c5b05b27 | ||
|
|
da13bcc74c | ||
|
|
363c142d25 | ||
|
|
5e33d2659d | ||
|
|
27a9df6d77 | ||
|
|
a78ea43be9 | ||
|
|
095150e0b7 | ||
|
|
7e4df1907e | ||
|
|
43c59bf6ff | ||
|
|
d0557b6af0 | ||
|
|
de587b3319 | ||
|
|
1f0957a873 | ||
|
|
dda2d5a79a | ||
|
|
01e33e18c3 | ||
|
|
ec5e5d3fcc | ||
|
|
13b06b760b | ||
|
|
38aba3e999 | ||
|
|
14a1c484bb | ||
|
|
f2b0388be8 | ||
|
|
788215fa83 | ||
|
|
7af9191cf3 | ||
|
|
66d6e2aa58 | ||
|
|
1d6d43f48a | ||
|
|
815c5556e0 | ||
|
|
14f8737c41 | ||
|
|
5b4e4bd7dc | ||
|
|
91f9b979d4 | ||
|
|
f80605aff8 | ||
|
|
e26c7406fb | ||
|
|
a547f2529a | ||
|
|
ad145ad503 | ||
|
|
ecc617f9eb | ||
|
|
485a01b78c | ||
|
|
f0ca3bf35d | ||
|
|
ac55482b18 | ||
|
|
8b9e430e5b | ||
|
|
6da9905235 | ||
|
|
18cc463c2c | ||
|
|
e7a1462a99 | ||
|
|
9e141c9f58 | ||
|
|
e5dbd26693 | ||
|
|
2272e3c4a7 | ||
|
|
8c6a9d4258 | ||
|
|
e4d202f1ce | ||
|
|
20349d9e8b | ||
|
|
96c142034e | ||
|
|
252b4f5644 | ||
|
|
067274973d | ||
|
|
30877344bd | ||
|
|
68dd0b37b5 | ||
|
|
e615deb1af | ||
|
|
e29d1ce3cf | ||
|
|
a5702746c5 | ||
|
|
f8e8830112 | ||
|
|
80def6663b | ||
|
|
7a8d10bb71 | ||
|
|
ce1b8bc2ec | ||
|
|
dadbc0d423 | ||
|
|
da31f90436 | ||
|
|
e55ad9019c | ||
|
|
59e111ab55 | ||
|
|
e00dd88add | ||
|
|
e18d78678f | ||
|
|
54ec3f4c92 | ||
|
|
9ac9551a2b | ||
|
|
0172cb4b39 | ||
|
|
856adebad5 | ||
|
|
033c7c27ad | ||
|
|
d134276d2d | ||
|
|
01976cec54 | ||
|
|
723cdf0a66 | ||
|
|
148087c508 | ||
|
|
88c5d0c7b7 | ||
|
|
a6c03a2aaa | ||
|
|
9df8445241 | ||
|
|
e5f4d52a5a | ||
|
|
7ef247c8db | ||
|
|
c582c8c67e | ||
|
|
74b5c59887 | ||
|
|
60efffdf2b | ||
|
|
b6379848ae | ||
|
|
b29919df96 | ||
|
|
052c0a0455 | ||
|
|
784e6f30fa | ||
|
|
5490cef129 | ||
|
|
459e8c9fcd | ||
|
|
ffa8414747 | ||
|
|
8130f88be8 | ||
|
|
c39e288c3e | ||
|
|
8158a16fbf | ||
|
|
57fe9cb77f | ||
|
|
e6249623d5 | ||
|
|
a9578637cb | ||
|
|
765193f223 | ||
|
|
3b019a0c9c | ||
|
|
5ac7272f5d | ||
|
|
4d8ab46e56 | ||
|
|
8468bb50dc | ||
|
|
1088ad8fe1 | ||
|
|
0b543d9f62 | ||
|
|
fa468d360a | ||
|
|
2759b1b69d | ||
|
|
1a0b7a8e31 | ||
|
|
1d01af0f8c | ||
|
|
f7d3c5e0c2 | ||
|
|
bc2bc35a8a | ||
|
|
15cb6bc7c6 | ||
|
|
aae70dfd21 | ||
|
|
ad12a48e9e | ||
|
|
09297fb72a | ||
|
|
b50a1b50b1 | ||
|
|
954ba0b5a7 | ||
|
|
015217c764 | ||
|
|
9a1ae677b8 | ||
|
|
6ee5174ba0 | ||
|
|
e843c6811c | ||
|
|
0d13044703 | ||
|
|
cfca3e7500 | ||
|
|
3c80c950ae | ||
|
|
182e3bb8a3 | ||
|
|
a627007a31 |
15
.github/workflows/build.yml
vendored
15
.github/workflows/build.yml
vendored
@@ -12,13 +12,14 @@ jobs:
|
|||||||
name: Build with ESP-IDF ${{ matrix.idf_ver }} for ${{ matrix.idf_target }}
|
name: Build with ESP-IDF ${{ matrix.idf_ver }} for ${{ matrix.idf_target }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
# The version names here correspond to the versions of espressif/idf Docker image.
|
# The version names here correspond to the versions of espressif/idf Docker image.
|
||||||
# See https://hub.docker.com/r/espressif/idf/tags and
|
# See https://hub.docker.com/r/espressif/idf/tags and
|
||||||
# https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/tools/idf-docker-image.html
|
# https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/tools/idf-docker-image.html
|
||||||
# for details.
|
# for details.
|
||||||
idf_ver: ["release-v4.4", "release-v5.1", "v5.3.2"]
|
idf_ver: ["release-v5.4", "release-v5.5"]
|
||||||
idf_target: ["esp32", "esp32s3", "esp32c2", "esp32c3", "esp32c6", "esp32h2", "esp32p4"]
|
idf_target: ["esp32", "esp32s3", "esp32c2", "esp32c3", "esp32c5", "esp32c6", "esp32c61", "esp32h2", "esp32p4"]
|
||||||
example:
|
example:
|
||||||
- NimBLE_Client
|
- NimBLE_Client
|
||||||
- NimBLE_Server
|
- NimBLE_Server
|
||||||
@@ -31,8 +32,14 @@ jobs:
|
|||||||
example: Bluetooth_5/NimBLE_extended_server
|
example: Bluetooth_5/NimBLE_extended_server
|
||||||
- idf_ver: release-v4.4
|
- idf_ver: release-v4.4
|
||||||
idf_target: "esp32c2"
|
idf_target: "esp32c2"
|
||||||
|
- idf_ver: release-v4.4
|
||||||
|
idf_target: "esp32c5"
|
||||||
- idf_ver: release-v4.4
|
- idf_ver: release-v4.4
|
||||||
idf_target: "esp32c6"
|
idf_target: "esp32c6"
|
||||||
|
- idf_ver: release-v4.4
|
||||||
|
idf_target: "esp32c61"
|
||||||
|
- idf_ver: release-v5.4
|
||||||
|
idf_target: "esp32c61"
|
||||||
- idf_ver: release-v4.4
|
- idf_ver: release-v4.4
|
||||||
idf_target: "esp32h2"
|
idf_target: "esp32h2"
|
||||||
- idf_ver: release-v4.4
|
- idf_ver: release-v4.4
|
||||||
@@ -43,7 +50,7 @@ jobs:
|
|||||||
container: espressif/idf:${{ matrix.idf_ver }}
|
container: espressif/idf:${{ matrix.idf_ver }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v5
|
||||||
with:
|
with:
|
||||||
path: components/esp-nimble-cpp
|
path: components/esp-nimble-cpp
|
||||||
- name: Build examples
|
- name: Build examples
|
||||||
@@ -58,7 +65,7 @@ jobs:
|
|||||||
build_docs:
|
build_docs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v5
|
||||||
- name: Doxygen Action
|
- name: Doxygen Action
|
||||||
uses: mattnotmitt/doxygen-action@v1.9.8
|
uses: mattnotmitt/doxygen-action@v1.9.8
|
||||||
with:
|
with:
|
||||||
|
|||||||
27
.github/workflows/release.yml
vendored
27
.github/workflows/release.yml
vendored
@@ -8,7 +8,7 @@ jobs:
|
|||||||
build_docs:
|
build_docs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v5
|
||||||
- name: Doxygen Action
|
- name: Doxygen Action
|
||||||
uses: mattnotmitt/doxygen-action@v1.9.8
|
uses: mattnotmitt/doxygen-action@v1.9.8
|
||||||
with:
|
with:
|
||||||
@@ -17,4 +17,27 @@ jobs:
|
|||||||
uses: peaceiris/actions-gh-pages@v4
|
uses: peaceiris/actions-gh-pages@v4
|
||||||
with:
|
with:
|
||||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
publish_dir: ./docs/doxydocs/html
|
publish_dir: ./docs/doxydocs/html
|
||||||
|
|
||||||
|
upload_to_component_registry:
|
||||||
|
permissions:
|
||||||
|
id-token: write
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v5
|
||||||
|
with:
|
||||||
|
submodules: "recursive"
|
||||||
|
- name: Upload component to the component registry
|
||||||
|
uses: espressif/upload-components-ci-action@v2
|
||||||
|
with:
|
||||||
|
components: "esp-nimble-cpp: ."
|
||||||
|
namespace: "h2zero"
|
||||||
|
|
||||||
|
upload_to_platformio_registry:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v5
|
||||||
|
- name: Upload component to PlatformIO Registry
|
||||||
|
uses: bojit/platformio-publish@main
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.PLATFORMIO_AUTH_TOKEN }}
|
||||||
|
|||||||
5
.gitignore
vendored
5
.gitignore
vendored
@@ -1 +1,4 @@
|
|||||||
docs/doxydocs
|
docs/doxydocs
|
||||||
|
dist
|
||||||
|
.development
|
||||||
|
_codeql_detected_source_root
|
||||||
|
|||||||
170
CHANGELOG.md
170
CHANGELOG.md
@@ -1,6 +1,176 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
|
## [2.4.0] 2026-03-20
|
||||||
|
|
||||||
|
## Fixed
|
||||||
|
- GATT attribute handles are now assigned from the registration callback so duplicate UUID attributes are identified correctly.
|
||||||
|
- Dynamic service changes now properly remove characteristics/descriptors and reset the GATT database when advertising starts.
|
||||||
|
- Missing notification/indication payload data when the value spans multiple mbufs, such as values larger than 255 bytes with small ACL buffers.
|
||||||
|
- `NimBLEDevice::createServer` will longer crash when called before the stack is initialized.
|
||||||
|
- Re-pairing after deleting all bonds now works by unpairing each stored bond instead of only deleting NVS data.
|
||||||
|
- Whitelist bounds checks.
|
||||||
|
- `NimBLEDevice::getBondedAddress` index bounds validation.
|
||||||
|
- Compiler warnings when bonds are disabled.
|
||||||
|
- kconfig warnings, redefined macros.
|
||||||
|
|
||||||
|
## Added
|
||||||
|
- `NimBLEStream`, `NimBLEStreamClient`, and `NimBLEStreamServer` classes and examples.
|
||||||
|
- `NimBLECppVersion.h` with compile-time version macros.
|
||||||
|
- `NimBLEDevice::getVersion` runtime version string helper.
|
||||||
|
- Matching passkey callbacks for both roles: `NimBLEServerCallbacks::onPassKeyEntry` and `NimBLEClientCallbacks::onPassKeyDisplay`.
|
||||||
|
- Bond migration helpers to convert bond storage between v1 and current formats while preserving existing bonds.
|
||||||
|
- `NimBLEUUID` constructor overload for `ble_uuid_t*`.
|
||||||
|
- Optional `index` parameter for `NimBLECharacteristic::getDescriptorByUUID` to access multiple descriptors with the same UUID.
|
||||||
|
- `NimBLEConnInfo::toString` method to get a string representation of the connection info.
|
||||||
|
|
||||||
|
## Changed
|
||||||
|
- `NimBLEService::start` is deprecated; services are now added when the server starts.
|
||||||
|
- `NimBLEHIDDevice::startServices()` is deprecated; services are now added when the server starts.
|
||||||
|
|
||||||
|
## [2.3.4] 2025-12-27
|
||||||
|
|
||||||
|
## Fixed
|
||||||
|
- Disconnect event not finding the client by address in some cases, use the connection handle instead.
|
||||||
|
- Notification/Indications will now be sent to server created clients.
|
||||||
|
- Attribute values will now consider type size when using a container.
|
||||||
|
- Descriptor searching will now correctly stop at the end handle of the characteristic.
|
||||||
|
|
||||||
|
## Changed
|
||||||
|
- Build conditionals now use `MYNEWT_VAL_` macros instead of `CONFIG_` macros where applicable.
|
||||||
|
- Sending of indications/notifications has been refactored for greater efficiency and tracks client subscription state locally instead uof relying on the stack.
|
||||||
|
|
||||||
|
## Added
|
||||||
|
- Support for esp32c61.
|
||||||
|
- `NimBLECharacteristicCallbacks::onStatus` overload that takes a `NimBLEConnInfo&` parameter to indicate which client the status is for.
|
||||||
|
- `NimBLEDevice::setCustomGapHandler` now takes a `void*` parameter to allow for passing user data to the custom handler.
|
||||||
|
|
||||||
|
## [2.3.3] 2025-09-05
|
||||||
|
|
||||||
|
## Fixed
|
||||||
|
- NimBLEAdvertisedDevice::isConnectable returning incorrect result.
|
||||||
|
- Extended advertisements not reporting full data.
|
||||||
|
|
||||||
|
## Added
|
||||||
|
- Support up to 1650 bytes of advertisement with extended advertising
|
||||||
|
|
||||||
|
## [2.3.2] 2025-09-02
|
||||||
|
|
||||||
|
## Fixed
|
||||||
|
- Build failures with esp-idf versions 4.x.
|
||||||
|
- Workaround for upstream issue causing onConnectFail to not be called.
|
||||||
|
- Build failures with idf v5.5+ and specific roles are not enabled.
|
||||||
|
|
||||||
|
## Changed
|
||||||
|
- Allow peripheral and central roles to be used without broadcaster/observer roles.
|
||||||
|
- Where applicable, `MYNEWT_VAL_` macros are used to control feature availability instead of `CONFIG_`
|
||||||
|
|
||||||
|
## [2.3.1] 2025-06-11
|
||||||
|
|
||||||
|
## Fixed
|
||||||
|
- Build errors when disabling BLE roles.
|
||||||
|
- `NimBLEClient::readValue` call not returning when the instance was created with a`NimBLEServer` and reading a secured characteristic.
|
||||||
|
- `NimBLEScan` destructor potentially causing a crash.
|
||||||
|
|
||||||
|
## Added
|
||||||
|
- `NimBLEBeacon::BeaconData` `std::vector<uint8_t>` operator to allow it to be used as a parameter to `NimBLEAdvertisementData::setManufacturerData`.
|
||||||
|
|
||||||
|
## [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
|
## [2.0.0] 2024-12-14
|
||||||
|
|
||||||
## **Breaking changes**
|
## **Breaking changes**
|
||||||
|
|||||||
@@ -35,7 +35,9 @@ idf_component_register(
|
|||||||
"esp32s3"
|
"esp32s3"
|
||||||
"esp32c2"
|
"esp32c2"
|
||||||
"esp32c3"
|
"esp32c3"
|
||||||
|
"esp32c5"
|
||||||
"esp32c6"
|
"esp32c6"
|
||||||
|
"esp32c61"
|
||||||
"esp32h2"
|
"esp32h2"
|
||||||
"esp32p4"
|
"esp32p4"
|
||||||
INCLUDE_DIRS
|
INCLUDE_DIRS
|
||||||
@@ -55,6 +57,8 @@ idf_component_register(
|
|||||||
"src/NimBLEEddystoneTLM.cpp"
|
"src/NimBLEEddystoneTLM.cpp"
|
||||||
"src/NimBLEExtAdvertising.cpp"
|
"src/NimBLEExtAdvertising.cpp"
|
||||||
"src/NimBLEHIDDevice.cpp"
|
"src/NimBLEHIDDevice.cpp"
|
||||||
|
"src/NimBLEL2CAPChannel.cpp"
|
||||||
|
"src/NimBLEL2CAPServer.cpp"
|
||||||
"src/NimBLERemoteCharacteristic.cpp"
|
"src/NimBLERemoteCharacteristic.cpp"
|
||||||
"src/NimBLERemoteDescriptor.cpp"
|
"src/NimBLERemoteDescriptor.cpp"
|
||||||
"src/NimBLERemoteService.cpp"
|
"src/NimBLERemoteService.cpp"
|
||||||
@@ -62,6 +66,7 @@ idf_component_register(
|
|||||||
"src/NimBLEScan.cpp"
|
"src/NimBLEScan.cpp"
|
||||||
"src/NimBLEServer.cpp"
|
"src/NimBLEServer.cpp"
|
||||||
"src/NimBLEService.cpp"
|
"src/NimBLEService.cpp"
|
||||||
|
"src/NimBLEStream.cpp"
|
||||||
"src/NimBLEUtils.cpp"
|
"src/NimBLEUtils.cpp"
|
||||||
"src/NimBLEUUID.cpp"
|
"src/NimBLEUUID.cpp"
|
||||||
REQUIRES
|
REQUIRES
|
||||||
|
|||||||
174
Kconfig
174
Kconfig
@@ -26,6 +26,113 @@ config NIMBLE_CPP_LOG_LEVEL
|
|||||||
default 3 if NIMBLE_CPP_LOG_LEVEL_INFO
|
default 3 if NIMBLE_CPP_LOG_LEVEL_INFO
|
||||||
default 4 if NIMBLE_CPP_LOG_LEVEL_DEBUG
|
default 4 if NIMBLE_CPP_LOG_LEVEL_DEBUG
|
||||||
|
|
||||||
|
config NIMBLE_CPP_LOG_OVERRIDE_COLOR
|
||||||
|
bool "Enable log color override."
|
||||||
|
default "n"
|
||||||
|
help
|
||||||
|
Enabling this option will allow NimBLE log levels to have
|
||||||
|
specific colors assigned.
|
||||||
|
|
||||||
|
menu "NIMBLE Log Override Colors"
|
||||||
|
depends on NIMBLE_CPP_LOG_OVERRIDE_COLOR
|
||||||
|
|
||||||
|
choice NIMBLE_CPP_LOG_OVERRIDE_COLOR_ERR
|
||||||
|
prompt "NimBLE CPP log override color Error"
|
||||||
|
default NIMBLE_CPP_LOG_OVERRIDE_COLOR_ERR_NONE
|
||||||
|
help
|
||||||
|
Select NimBLE CPP log override error color.
|
||||||
|
|
||||||
|
config NIMBLE_CPP_LOG_OVERRIDE_COLOR_ERR_NONE
|
||||||
|
bool "None"
|
||||||
|
config NIMBLE_CPP_LOG_OVERRIDE_COLOR_ERR_BLACK
|
||||||
|
bool "Black"
|
||||||
|
config NIMBLE_CPP_LOG_OVERRIDE_COLOR_ERR_RED
|
||||||
|
bool "Red"
|
||||||
|
config NIMBLE_CPP_LOG_OVERRIDE_COLOR_ERR_GREEN
|
||||||
|
bool "Green"
|
||||||
|
config NIMBLE_CPP_LOG_OVERRIDE_COLOR_ERR_YELLOW
|
||||||
|
bool "Yellow"
|
||||||
|
config NIMBLE_CPP_LOG_OVERRIDE_COLOR_ERR_BLUE
|
||||||
|
bool "Blue"
|
||||||
|
config NIMBLE_CPP_LOG_OVERRIDE_COLOR_ERR_PURPLE
|
||||||
|
bool "Purple"
|
||||||
|
config NIMBLE_CPP_LOG_OVERRIDE_COLOR_ERR_CYAN
|
||||||
|
bool "Cyan"
|
||||||
|
endchoice #NIMBLE_CPP_LOG_OVERRIDE_COLOR_ERR
|
||||||
|
|
||||||
|
choice NIMBLE_CPP_LOG_OVERRIDE_COLOR_WARN
|
||||||
|
prompt "NimBLE CPP log override color Warning"
|
||||||
|
default NIMBLE_CPP_LOG_OVERRIDE_COLOR_WARN_NONE
|
||||||
|
help
|
||||||
|
Select NimBLE CPP log override warning color.
|
||||||
|
|
||||||
|
config NIMBLE_CPP_LOG_OVERRIDE_COLOR_WARN_NONE
|
||||||
|
bool "None"
|
||||||
|
config NIMBLE_CPP_LOG_OVERRIDE_COLOR_WARN_BLACK
|
||||||
|
bool "Black"
|
||||||
|
config NIMBLE_CPP_LOG_OVERRIDE_COLOR_WARN_RED
|
||||||
|
bool "Red"
|
||||||
|
config NIMBLE_CPP_LOG_OVERRIDE_COLOR_WARN_GREEN
|
||||||
|
bool "Green"
|
||||||
|
config NIMBLE_CPP_LOG_OVERRIDE_COLOR_WARN_YELLOW
|
||||||
|
bool "Yellow"
|
||||||
|
config NIMBLE_CPP_LOG_OVERRIDE_COLOR_WARN_BLUE
|
||||||
|
bool "Blue"
|
||||||
|
config NIMBLE_CPP_LOG_OVERRIDE_COLOR_WARN_PURPLE
|
||||||
|
bool "Purple"
|
||||||
|
config NIMBLE_CPP_LOG_OVERRIDE_COLOR_WARN_CYAN
|
||||||
|
bool "Cyan"
|
||||||
|
endchoice #NIMBLE_CPP_LOG_OVERRIDE_COLOR_WARN
|
||||||
|
|
||||||
|
choice NIMBLE_CPP_LOG_OVERRIDE_COLOR_INFO
|
||||||
|
prompt "NimBLE CPP log override color Info"
|
||||||
|
default NIMBLE_CPP_LOG_OVERRIDE_COLOR_INFO_NONE
|
||||||
|
help
|
||||||
|
Select NimBLE CPP log override info color.
|
||||||
|
|
||||||
|
config NIMBLE_CPP_LOG_OVERRIDE_COLOR_INFO_NONE
|
||||||
|
bool "None"
|
||||||
|
config NIMBLE_CPP_LOG_OVERRIDE_COLOR_INFO_BLACK
|
||||||
|
bool "Black"
|
||||||
|
config NIMBLE_CPP_LOG_OVERRIDE_COLOR_INFO_RED
|
||||||
|
bool "Red"
|
||||||
|
config NIMBLE_CPP_LOG_OVERRIDE_COLOR_INFO_GREEN
|
||||||
|
bool "Green"
|
||||||
|
config NIMBLE_CPP_LOG_OVERRIDE_COLOR_INFO_YELLOW
|
||||||
|
bool "Yellow"
|
||||||
|
config NIMBLE_CPP_LOG_OVERRIDE_COLOR_INFO_BLUE
|
||||||
|
bool "Blue"
|
||||||
|
config NIMBLE_CPP_LOG_OVERRIDE_COLOR_INFO_PURPLE
|
||||||
|
bool "Purple"
|
||||||
|
config NIMBLE_CPP_LOG_OVERRIDE_COLOR_INFO_CYAN
|
||||||
|
bool "Cyan"
|
||||||
|
endchoice #NIMBLE_CPP_LOG_OVERRIDE_COLOR_INFO
|
||||||
|
|
||||||
|
choice NIMBLE_CPP_LOG_OVERRIDE_COLOR_DEBUG
|
||||||
|
prompt "NimBLE CPP log override color Debug"
|
||||||
|
default NIMBLE_CPP_LOG_OVERRIDE_COLOR_DEBUG_NONE
|
||||||
|
help
|
||||||
|
Select NimBLE CPP log override debug color.
|
||||||
|
|
||||||
|
config NIMBLE_CPP_LOG_OVERRIDE_COLOR_DEBUG_NONE
|
||||||
|
bool "None"
|
||||||
|
config NIMBLE_CPP_LOG_OVERRIDE_COLOR_DEBUG_BLACK
|
||||||
|
bool "Black"
|
||||||
|
config NIMBLE_CPP_LOG_OVERRIDE_COLOR_DEBUG_RED
|
||||||
|
bool "Red"
|
||||||
|
config NIMBLE_CPP_LOG_OVERRIDE_COLOR_DEBUG_GREEN
|
||||||
|
bool "Green"
|
||||||
|
config NIMBLE_CPP_LOG_OVERRIDE_COLOR_DEBUG_YELLOW
|
||||||
|
bool "Yellow"
|
||||||
|
config NIMBLE_CPP_LOG_OVERRIDE_COLOR_DEBUG_BLUE
|
||||||
|
bool "Blue"
|
||||||
|
config NIMBLE_CPP_LOG_OVERRIDE_COLOR_DEBUG_PURPLE
|
||||||
|
bool "Purple"
|
||||||
|
config NIMBLE_CPP_LOG_OVERRIDE_COLOR_DEBUG_CYAN
|
||||||
|
bool "Cyan"
|
||||||
|
endchoice #NIMBLE_CPP_LOG_OVERRIDE_COLOR_DEBUG
|
||||||
|
endmenu
|
||||||
|
|
||||||
config NIMBLE_CPP_ENABLE_RETURN_CODE_TEXT
|
config NIMBLE_CPP_ENABLE_RETURN_CODE_TEXT
|
||||||
bool "Show NimBLE return codes as text in debug log."
|
bool "Show NimBLE return codes as text in debug log."
|
||||||
default "n"
|
default "n"
|
||||||
@@ -50,6 +157,20 @@ config NIMBLE_CPP_ENABLE_ADVERTISEMENT_TYPE_TEXT
|
|||||||
while scanning as text messages in the debug log.
|
while scanning as text messages in the debug log.
|
||||||
This will use approximately 250 bytes of flash memory.
|
This will use approximately 250 bytes of flash memory.
|
||||||
|
|
||||||
|
config NIMBLE_CPP_ADDR_FMT_EXCLUDE_DELIMITER
|
||||||
|
bool "Exclude colon characters when printing address."
|
||||||
|
default "n"
|
||||||
|
help
|
||||||
|
Enabling this option will format MAC addresses without
|
||||||
|
colon characters when printing.
|
||||||
|
|
||||||
|
config NIMBLE_CPP_ADDR_FMT_UPPERCASE
|
||||||
|
bool "Use uppercase letters when printing address."
|
||||||
|
default "n"
|
||||||
|
help
|
||||||
|
Enabling this option will format MAC addresses in
|
||||||
|
uppercase letters when printing.
|
||||||
|
|
||||||
config NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED
|
config NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED
|
||||||
bool "Enable timestamps to be stored with attribute values."
|
bool "Enable timestamps to be stored with attribute values."
|
||||||
default "n"
|
default "n"
|
||||||
@@ -77,57 +198,14 @@ config NIMBLE_CPP_DEBUG_ASSERT_ENABLED
|
|||||||
This will use approximately 1kB of flash memory.
|
This will use approximately 1kB of flash memory.
|
||||||
|
|
||||||
config NIMBLE_CPP_FREERTOS_TASK_BLOCK_BIT
|
config NIMBLE_CPP_FREERTOS_TASK_BLOCK_BIT
|
||||||
int "FreeRTOS task block bit"
|
int "FreeRTOS task block bit."
|
||||||
default 31
|
default 31
|
||||||
help
|
help
|
||||||
Configure the bit to set in the task notification value when a task is blocked waiting for an event.
|
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.
|
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 NIMBLE_CPP_IDF
|
||||||
|
bool
|
||||||
|
default BT_NIMBLE_ENABLED
|
||||||
|
|
||||||
config BT_NIMBLE_ENABLED
|
endmenu
|
||||||
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
|
|
||||||
2
NOTICE
2
NOTICE
@@ -1,6 +1,6 @@
|
|||||||
esp-nimble-cpp
|
esp-nimble-cpp
|
||||||
NimBLE-Arduino
|
NimBLE-Arduino
|
||||||
Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
|
Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
|
||||||
esp-nimble-cpp, NimBLE-Arduino contributors.
|
esp-nimble-cpp, NimBLE-Arduino contributors.
|
||||||
|
|
||||||
The Initial Developer of some parts of this library, which are copied from,
|
The Initial Developer of some parts of this library, which are copied from,
|
||||||
|
|||||||
@@ -9,6 +9,8 @@
|
|||||||
|
|
||||||
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).
|
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).
|
||||||
|
|
||||||
|
The library is threadsafe, characteristics can be set from any thread.
|
||||||
|
|
||||||
**An Arduino version of this library, including NimBLE, can be [found here.](https://github.com/h2zero/NimBLE-Arduino)**
|
**An Arduino version of this library, including NimBLE, can be [found here.](https://github.com/h2zero/NimBLE-Arduino)**
|
||||||
|
|
||||||
This library **significantly** reduces resource usage and improves performance for ESP32 BLE applications as compared
|
This library **significantly** reduces resource usage and improves performance for ESP32 BLE applications as compared
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ The changes listed here are only the required changes that must be made, and a s
|
|||||||
Note: If setting a custom address, it should be set with `NimBLEDevice::setOwnAddr` first before calling `NimBLEDevice::setOwnAddrType`.
|
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::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`.
|
- `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`.
|
||||||
|
- `NimBLEDevice::getClientByID(uint16_t conn_id);` has been changed to `NimBLEDevice::getClientByHandle(uint16_t connHandle)`
|
||||||
<br/>
|
<br/>
|
||||||
|
|
||||||
## BLE Addresses
|
## BLE Addresses
|
||||||
@@ -54,7 +55,7 @@ The default address type is public `0`, whereas many devices use a random `1` ad
|
|||||||
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)`
|
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`.
|
`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`.
|
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.val` and type is in `ble_addr_t.type`.
|
||||||
<br/>
|
<br/>
|
||||||
|
|
||||||
## BLE UUID's
|
## BLE UUID's
|
||||||
@@ -65,7 +66,8 @@ This returns a pointer to `const ble_addr_t` instead of a pointer to the address
|
|||||||
## Server
|
## Server
|
||||||
- `NimBLEServer::disconnect` now returns `bool`, true = success, instead of `int` to be consistent with the rest of the library.
|
- `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.
|
- `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.
|
- `NimBLEServer::getPeerIDInfo` renamed to `NimBLEServer::getPeerInfoByHandle` to better describe it's use.
|
||||||
|
- Advertising is no longer automatically restarted when a peer disconnects, to re-enable this feature either call `NimBLEServer::advertiseOnDisconnect(true);` after creating the server or manually restart advertising in the `onDisconnect` callback.
|
||||||
<br/>
|
<br/>
|
||||||
|
|
||||||
### Services
|
### Services
|
||||||
@@ -132,6 +134,7 @@ Have been removed, instead the application should use `NimBLERemoteCharacteristi
|
|||||||
- - `NimBLEScanCallbacks::onResult`, functions the same as the old `NimBLEAdvertisedDeviceCallbacks::onResult` but now takes aa `const NimBLEAdvertisedDevice*` instead of non-const.
|
- - `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::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.
|
- - `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::setAdvertisedDeviceCallbacks(NimBLEAdvertisedDeviceCallbacks* callbacks, bool wantDuplicates)` has been changed to `NimBLEScan::setScanCallbacks(NimBLEScanCallbacks* callbacks, bool wantDuplicates);`
|
||||||
<br/>
|
<br/>
|
||||||
|
|
||||||
### Advertised Device
|
### Advertised Device
|
||||||
|
|||||||
@@ -48,8 +48,7 @@ PROJECT_NAME = esp-nimble-cpp
|
|||||||
# could be handy for archiving the generated documentation or if some version
|
# could be handy for archiving the generated documentation or if some version
|
||||||
# control system is used.
|
# control system is used.
|
||||||
|
|
||||||
PROJECT_NUMBER = 2.0.0
|
PROJECT_NUMBER = 2.4.0
|
||||||
|
|
||||||
# Using the PROJECT_BRIEF tag one can provide an optional one line description
|
# Using the PROJECT_BRIEF tag one can provide an optional one line description
|
||||||
# for a project that appears at the top of each page and should give viewer a
|
# for a project that appears at the top of each page and should give viewer a
|
||||||
# quick idea about the purpose of the project. Keep the description short.
|
# quick idea about the purpose of the project. Keep the description short.
|
||||||
@@ -2417,12 +2416,12 @@ INCLUDE_FILE_PATTERNS =
|
|||||||
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
|
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
|
||||||
|
|
||||||
PREDEFINED = _DOXYGEN_ \
|
PREDEFINED = _DOXYGEN_ \
|
||||||
CONFIG_BT_ENABLED \
|
CONFIG_BT_NIMBLE_ENABLED=1 \
|
||||||
CONFIG_BT_NIMBLE_ROLE_CENTRAL \
|
MYNEWT_VAL_BLE_ROLE_CENTRAL=1 \
|
||||||
CONFIG_BT_NIMBLE_ROLE_OBSERVER \
|
MYNEWT_VAL_BLE_ROLE_OBSERVER=1 \
|
||||||
CONFIG_BT_NIMBLE_ROLE_PERIPHERAL \
|
MYNEWT_VAL_BLE_ROLE_PERIPHERAL=1 \
|
||||||
CONFIG_BT_NIMBLE_ROLE_BROADCASTER \
|
MYNEWT_VAL_BLE_ROLE_BROADCASTER=1 \
|
||||||
CONFIG_BT_NIMBLE_EXT_ADV
|
MYNEWT_VAL_BLE_EXT_ADV=1
|
||||||
|
|
||||||
# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
|
# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
|
||||||
# tag can be used to specify a list of macro names that should be expanded. The
|
# tag can be used to specify a list of macro names that should be expanded. The
|
||||||
|
|||||||
@@ -234,7 +234,7 @@ This can be changed to use passkey authentication or numeric comparison. See [Se
|
|||||||
Advertising works the same as the original API except:
|
Advertising works the same as the original API except:
|
||||||
|
|
||||||
Calling `NimBLEAdvertising::setAdvertisementData` will entirely replace any data set with `NimBLEAdvertising::addServiceUUID`, or
|
Calling `NimBLEAdvertising::setAdvertisementData` will entirely replace any data set with `NimBLEAdvertising::addServiceUUID`, or
|
||||||
`NimBLEAdvertising::setAppearance` or similar methods. You should set all the data you wish to advertise within the `NimBLEAdvertisementData` instead.
|
`NimBLEAdvertising::setAppearance` or similar methods. You should set all the data you wish to advertise within the `NimBLEAdvertisementData` instead if calling `NimBLEAdvertising::setAdvertisementData`.
|
||||||
<br/>
|
<br/>
|
||||||
|
|
||||||
> BLEAdvertising::start (NimBLEAdvertising::start)
|
> BLEAdvertising::start (NimBLEAdvertising::start)
|
||||||
|
|||||||
@@ -37,9 +37,7 @@ For this example we will keep it simple and use a 16 bit value: ABCD.
|
|||||||
```
|
```
|
||||||
#include "NimBLEDevice.h"
|
#include "NimBLEDevice.h"
|
||||||
|
|
||||||
// void setup() in Arduino
|
extern "C" void app_main(void) {
|
||||||
void app_main(void)
|
|
||||||
{
|
|
||||||
NimBLEDevice::init("NimBLE");
|
NimBLEDevice::init("NimBLE");
|
||||||
|
|
||||||
NimBLEServer *pServer = NimBLEDevice::createServer();
|
NimBLEServer *pServer = NimBLEDevice::createServer();
|
||||||
@@ -79,9 +77,7 @@ The function call will simply be `pService->createCharacteristic("1234");`
|
|||||||
```
|
```
|
||||||
#include "NimBLEDevice.h"
|
#include "NimBLEDevice.h"
|
||||||
|
|
||||||
// void setup() in Arduino
|
extern "C" void app_main(void) {
|
||||||
void app_main(void)
|
|
||||||
{
|
|
||||||
NimBLEDevice::init("NimBLE");
|
NimBLEDevice::init("NimBLE");
|
||||||
|
|
||||||
NimBLEServer *pServer = NimBLEDevice::createServer();
|
NimBLEServer *pServer = NimBLEDevice::createServer();
|
||||||
@@ -99,12 +95,13 @@ There are many different types you can send as parameters for the value but for
|
|||||||
`pCharacteristic->setValue("Hello BLE");`
|
`pCharacteristic->setValue("Hello BLE");`
|
||||||
|
|
||||||
Next we need to advertise for connections.
|
Next we need to advertise for connections.
|
||||||
To do this we create an instance of `NimBLEAdvertising` add our service to it (optional) and start advertisng.
|
To do this we create an instance of `NimBLEAdvertising` add our service to it (optional) and start advertising.
|
||||||
|
|
||||||
**The code for this will be:**
|
**The code for this will be:**
|
||||||
```
|
```
|
||||||
NimBLEAdvertising *pAdvertising = NimBLEDevice::getAdvertising(); // create advertising instance
|
NimBLEAdvertising *pAdvertising = NimBLEDevice::getAdvertising(); // create advertising instance
|
||||||
pAdvertising->addServiceUUID("ABCD"); // tell advertising the UUID of our service
|
pAdvertising->addServiceUUID("ABCD"); // advertise the UUID of our service
|
||||||
|
pAdvertising->setName("NimBLE"); // advertise the device name
|
||||||
pAdvertising->start(); // start advertising
|
pAdvertising->start(); // start advertising
|
||||||
```
|
```
|
||||||
That's it, this will be enough to create a BLE server with a service and a characteristic and advertise for client connections.
|
That's it, this will be enough to create a BLE server with a service and a characteristic and advertise for client connections.
|
||||||
@@ -113,9 +110,7 @@ That's it, this will be enough to create a BLE server with a service and a chara
|
|||||||
```
|
```
|
||||||
#include "NimBLEDevice.h"
|
#include "NimBLEDevice.h"
|
||||||
|
|
||||||
// void setup() in Arduino
|
extern "C" void app_main(void) {
|
||||||
void app_main(void)
|
|
||||||
{
|
|
||||||
NimBLEDevice::init("NimBLE");
|
NimBLEDevice::init("NimBLE");
|
||||||
|
|
||||||
NimBLEServer *pServer = NimBLEDevice::createServer();
|
NimBLEServer *pServer = NimBLEDevice::createServer();
|
||||||
@@ -126,7 +121,8 @@ void app_main(void)
|
|||||||
pCharacteristic->setValue("Hello BLE");
|
pCharacteristic->setValue("Hello BLE");
|
||||||
|
|
||||||
NimBLEAdvertising *pAdvertising = NimBLEDevice::getAdvertising();
|
NimBLEAdvertising *pAdvertising = NimBLEDevice::getAdvertising();
|
||||||
pAdvertising->addServiceUUID("ABCD");
|
pAdvertising->addServiceUUID("ABCD"); // advertise the UUID of our service
|
||||||
|
pAdvertising->setName("NimBLE"); // advertise the device name
|
||||||
pAdvertising->start();
|
pAdvertising->start();
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -144,7 +140,7 @@ After initializing the NimBLE stack we create a scan instance by calling `NimBLE
|
|||||||
|
|
||||||
Once we have created the scan we can start looking for advertising servers.
|
Once we have created the scan we can start looking for advertising servers.
|
||||||
|
|
||||||
To do this we call `NimBLEScan::start(duration)`, the duration parameter is a uint32_t that specifies the number of milliseconds to scan for,
|
To do this we call `NimBLEScan::getResults(duration)`, the duration parameter is a uint32_t that specifies the number of milliseconds to scan for,
|
||||||
passing 0 will scan forever.
|
passing 0 will scan forever.
|
||||||
|
|
||||||
In this example we will scan for 10 seconds. This is a blocking function (a non blocking overload is also available).
|
In this example we will scan for 10 seconds. This is a blocking function (a non blocking overload is also available).
|
||||||
@@ -154,9 +150,7 @@ This call returns an instance of `NimBLEScanResults` when the scan completes whi
|
|||||||
```
|
```
|
||||||
#include "NimBLEDevice.h"
|
#include "NimBLEDevice.h"
|
||||||
|
|
||||||
// void setup() in Arduino
|
extern "C" void app_main(void) {
|
||||||
void app_main(void)
|
|
||||||
{
|
|
||||||
NimBLEDevice::init("");
|
NimBLEDevice::init("");
|
||||||
|
|
||||||
NimBLEScan *pScan = NimBLEDevice::getScan();
|
NimBLEScan *pScan = NimBLEDevice::getScan();
|
||||||
@@ -168,7 +162,7 @@ void app_main(void)
|
|||||||
Now that we have scanned we need to check the results for any advertisers we are interested in connecting to.
|
Now that we have scanned we need to check the results for any advertisers we are interested in connecting to.
|
||||||
|
|
||||||
To do this we iterate through the results and check if any of the devices found are advertising the service we want `ABCD`.
|
To do this we iterate through the results and check if any of the devices found are advertising the service we want `ABCD`.
|
||||||
Each result in `NimBLEScanResults` is a `NimBLEAdvertisedDevice` instance that we can access data from.
|
Each result in `NimBLEScanResults` is a `const NimBLEAdvertisedDevice*` that we can access data from.
|
||||||
|
|
||||||
We will check each device found for the `ABCD` service by calling `NimBLEAdvertisedDevice::isAdvertisingService`.
|
We will check each device found for the `ABCD` service by calling `NimBLEAdvertisedDevice::isAdvertisingService`.
|
||||||
This takes an instance of `NimBLEUUID` as a parameter so we will need to create one.
|
This takes an instance of `NimBLEUUID` as a parameter so we will need to create one.
|
||||||
@@ -177,11 +171,11 @@ This takes an instance of `NimBLEUUID` as a parameter so we will need to create
|
|||||||
```
|
```
|
||||||
NimBLEUUID serviceUuid("ABCD");
|
NimBLEUUID serviceUuid("ABCD");
|
||||||
|
|
||||||
for(int i = 0; i < results.getCount(); i++) {
|
for (int i = 0; i < results.getCount(); i++) {
|
||||||
NimBLEAdvertisedDevice device = results.getDevice(i);
|
const NimBLEAdvertisedDevice *device = results.getDevice(i);
|
||||||
|
|
||||||
if (device.isAdvertisingService(serviceUuid)) {
|
if (device->isAdvertisingService(serviceUuid)) {
|
||||||
// create a client and connect
|
// create a client and connect
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -198,16 +192,16 @@ This takes a pointer to the `NimBLEAdvertisedDevice` and returns `true` if succe
|
|||||||
```
|
```
|
||||||
NimBLEUUID serviceUuid("ABCD");
|
NimBLEUUID serviceUuid("ABCD");
|
||||||
|
|
||||||
for(int i = 0; i < results.getCount(); i++) {
|
for (int i = 0; i < results.getCount(); i++) {
|
||||||
NimBLEAdvertisedDevice device = results.getDevice(i);
|
const NimBLEAdvertisedDevice *device = results.getDevice(i);
|
||||||
|
|
||||||
if (device.isAdvertisingService(serviceUuid)) {
|
if (device->isAdvertisingService(serviceUuid)) {
|
||||||
NimBLEClient *pClient = NimBLEDevice::createClient();
|
NimBLEClient *pClient = NimBLEDevice::createClient();
|
||||||
|
|
||||||
if(pClient->connect(&device)) {
|
if (pClient->connect(&device)) {
|
||||||
//success
|
//success
|
||||||
} else {
|
} else {
|
||||||
// failed to connect
|
// failed to connect
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -229,11 +223,15 @@ Finally we will read the characteristic value with `NimBLERemoteCharacteristic::
|
|||||||
```
|
```
|
||||||
NimBLEUUID serviceUuid("ABCD");
|
NimBLEUUID serviceUuid("ABCD");
|
||||||
|
|
||||||
for(int i = 0; i < results.getCount(); i++) {
|
for (int i = 0; i < results.getCount(); i++) {
|
||||||
NimBLEAdvertisedDevice device = results.getDevice(i);
|
const NimBLEAdvertisedDevice *device = results.getDevice(i);
|
||||||
|
|
||||||
if (device.isAdvertisingService(serviceUuid)) {
|
if (device->isAdvertisingService(serviceUuid)) {
|
||||||
NimBLEClient *pClient = NimBLEDevice::createClient();
|
NimBLEClient *pClient = NimBLEDevice::createClient();
|
||||||
|
|
||||||
|
if (!pClient) { // Make sure the client was created
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (pClient->connect(&device)) {
|
if (pClient->connect(&device)) {
|
||||||
NimBLERemoteService *pService = pClient->getService(serviceUuid);
|
NimBLERemoteService *pService = pClient->getService(serviceUuid);
|
||||||
@@ -247,7 +245,7 @@ for(int i = 0; i < results.getCount(); i++) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// failed to connect
|
// failed to connect
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -262,12 +260,16 @@ This is done by calling `NimBLEDevice::deleteClient`.
|
|||||||
```
|
```
|
||||||
NimBLEUUID serviceUuid("ABCD");
|
NimBLEUUID serviceUuid("ABCD");
|
||||||
|
|
||||||
for(int i = 0; i < results.getCount(); i++) {
|
for (int i = 0; i < results.getCount(); i++) {
|
||||||
NimBLEAdvertisedDevice device = results.getDevice(i);
|
const NimBLEAdvertisedDevice *device = results.getDevice(i);
|
||||||
|
|
||||||
if (device.isAdvertisingService(serviceUuid)) {
|
if (device->isAdvertisingService(serviceUuid)) {
|
||||||
NimBLEClient *pClient = NimBLEDevice::createClient();
|
NimBLEClient *pClient = NimBLEDevice::createClient();
|
||||||
|
|
||||||
|
if (!pClient) { // Make sure the client was created
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (pClient->connect(&device)) {
|
if (pClient->connect(&device)) {
|
||||||
NimBLERemoteService *pService = pClient->getService(serviceUuid);
|
NimBLERemoteService *pService = pClient->getService(serviceUuid);
|
||||||
|
|
||||||
@@ -280,7 +282,7 @@ for(int i = 0; i < results.getCount(); i++) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// failed to connect
|
// failed to connect
|
||||||
}
|
}
|
||||||
|
|
||||||
NimBLEDevice::deleteClient(pClient);
|
NimBLEDevice::deleteClient(pClient);
|
||||||
@@ -294,37 +296,39 @@ Note that there is no need to disconnect as that will be done when deleting the
|
|||||||
```
|
```
|
||||||
#include "NimBLEDevice.h"
|
#include "NimBLEDevice.h"
|
||||||
|
|
||||||
// void setup() in Arduino
|
extern "C" void app_main(void) {
|
||||||
void app_main(void)
|
|
||||||
{
|
|
||||||
NimBLEDevice::init("");
|
NimBLEDevice::init("");
|
||||||
|
|
||||||
NimBLEScan *pScan = NimBLEDevice::getScan();
|
NimBLEScan *pScan = NimBLEDevice::getScan();
|
||||||
NimBLEScanResults results = pScan->start(10 * 1000);
|
NimBLEScanResults results = pScan->getResults(10 * 1000);
|
||||||
|
|
||||||
NimBLEUUID serviceUuid("ABCD");
|
NimBLEUUID serviceUuid("ABCD");
|
||||||
|
|
||||||
for(int i = 0; i < results.getCount(); i++) {
|
for (int i = 0; i < results.getCount(); i++) {
|
||||||
NimBLEAdvertisedDevice device = results.getDevice(i);
|
const NimBLEAdvertisedDevice *device = results.getDevice(i);
|
||||||
|
|
||||||
if (device.isAdvertisingService(serviceUuid)) {
|
if (device->isAdvertisingService(serviceUuid)) {
|
||||||
NimBLEClient *pClient = NimBLEDevice::createClient();
|
NimBLEClient *pClient = NimBLEDevice::createClient();
|
||||||
|
|
||||||
|
if (!pClient) { // Make sure the client was created
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (pClient->connect(&device)) {
|
if (pClient->connect(&device)) {
|
||||||
NimBLERemoteService *pService = pClient->getService(serviceUuid);
|
NimBLERemoteService *pService = pClient->getService(serviceUuid);
|
||||||
|
|
||||||
if (pService != nullptr) {
|
if (pService != nullptr) {
|
||||||
NimBLERemoteCharacteristic *pCharacteristic = pService->getCharacteristic("1234");
|
NimBLERemoteCharacteristic *pCharacteristic = pService->getCharacteristic("1234");
|
||||||
|
|
||||||
if (pCharacteristic != nullptr) {
|
if (pCharacteristic != nullptr) {
|
||||||
std::string value = pCharacteristic->readValue();
|
std::string value = pCharacteristic->readValue();
|
||||||
// print or do whatever you need with the value
|
// print or do whatever you need with the value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// failed to connect
|
// failed to connect
|
||||||
}
|
}
|
||||||
|
|
||||||
NimBLEDevice::deleteClient(pClient);
|
NimBLEDevice::deleteClient(pClient);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -334,4 +338,3 @@ void app_main(void)
|
|||||||
|
|
||||||
For more advanced features and options please see the client examples in the examples folder.
|
For more advanced features and options please see the client examples in the examples folder.
|
||||||
<br/>
|
<br/>
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,8 @@
|
|||||||
# Usage Tips
|
# Usage Tips
|
||||||
|
|
||||||
## Put BLE functions in a task running on the NimBLE stack core
|
## Threadsafety
|
||||||
|
|
||||||
When commands are sent to the stack from a different core they can experience delays in execution.
|
This library is threadsafe. Attribues can be manipulated freely.
|
||||||
This library detects this and invokes the esp32 IPC to reroute these commands through the correct core but this also increases overhead.
|
|
||||||
Therefore it is highly recommended to create tasks for BLE to run on the same core, the macro `CONFIG_BT_NIMBLE_PINNED_TO_CORE` can be used to set the core.
|
|
||||||
<br/>
|
|
||||||
|
|
||||||
## Do not delete client instances unless necessary or unused
|
## Do not delete client instances unless necessary or unused
|
||||||
|
|
||||||
@@ -32,6 +29,41 @@ or nullptr such as when calling `NimBLEClient::getService`. The latter being a
|
|||||||
Most of the functions in this library return something that should be checked before proceeding.
|
Most of the functions in this library return something that should be checked before proceeding.
|
||||||
<br/>
|
<br/>
|
||||||
|
|
||||||
|
## Persisted bonds can be lost due to low MAX_CCCDS
|
||||||
|
|
||||||
|
The symptom: CONFIG_BT_NIMBLE_MAX_BONDS is set to N, but a smaller number of bonds is preserved, perhaps even a single bond. The limitation of persisted bonds can be observed via NimBLEDevice::getNumBonds(). The number may not reach CONFIG_BT_NIMBLE_MAX_BONDS.
|
||||||
|
|
||||||
|
Cause: For each bond, NimBLE persists each of the CCCD (client characteristic configuration descriptor) values that have been subscribed
|
||||||
|
to by the client. If CONFIG_BT_NIMBLE_MAX_CCCDS is too low, the older CCCD values are overwritten by the newer ones. The loss of the older
|
||||||
|
CCCDs values results in those bonds being lost.
|
||||||
|
|
||||||
|
Fix: Increase CONFIG_BT_NIMBLE_MAX_CCCDS. These take approximately 40 bytes in NVS, 2 bytes for the CCCD value and the NVS metadata overhead. The value of
|
||||||
|
CONFIG_BT_NIMBLE_MAX_CCCDS should conservatively be no less than (CONFIG_BT_NIMBLE_MAX_BONDS * {maximum number of characteristics that can be subscribed to}).
|
||||||
|
|
||||||
|
## Device 'Local Name'
|
||||||
|
|
||||||
|
'Local name' refers to how the device is seen and displayed.
|
||||||
|
|
||||||
|
A devices 'Local name' can be thought of as coming from two places, the <i>Advertising "Local name"</i> and the <i>the GATT Device Name</i>.
|
||||||
|
|
||||||
|
### Advertising "Local name"
|
||||||
|
|
||||||
|
Field found in the advertising data payload. Value is set via NimBLEAdvertising::setName().
|
||||||
|
|
||||||
|
### GATT Device Name
|
||||||
|
|
||||||
|
Characteristic UUID 0x2A00 in the Generic Access service. Set via NimBLEDevice::init() or NimBLEDevice::setDeviceName().
|
||||||
|
|
||||||
|
This characteristic is read <b>after</b> connecting to the device.
|
||||||
|
|
||||||
|
### Important considerations
|
||||||
|
|
||||||
|
* OSes cache the <i>'GATT Device Name'</i>.
|
||||||
|
* OSes update the device name based on the <i>'GATT Device Name'</i> after connecting to a device. This means that if you set the <i>Advertising 'Local name'</i> to "ABCD" but the <i>'GATT Device Name'</i> to "12345", the device will be seen as "ABCD" until connecting to the device, at which time the devices name will change to "12345".
|
||||||
|
* If no <i>'Advertising "Local name"'</i> is set, OSes, such as iOS, may display the devices name as 'Unnamed' until the device is connected to, at which time the <i>'GATT Device Name'</i> is read and used instead.
|
||||||
|
|
||||||
|
It is recommended that both <i>'Advertising "Local name"'</i> <b>and</b> <i>'GATT Device Name'</i> be set appropriately, after considering the above described behavior.
|
||||||
|
|
||||||
## There will be bugs - please report them
|
## There will be bugs - please report them
|
||||||
|
|
||||||
No code is bug free and unit testing will not find them all on it's own. If you encounter a bug, please report it along with any logs and decoded backtrace if applicable.
|
No code is bug free and unit testing will not find them all on it's own. If you encounter a bug, please report it along with any logs and decoded backtrace if applicable.
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ static uint8_t secondaryPhy = BLE_HCI_LE_PHY_1M;
|
|||||||
/** Handler class for server events */
|
/** Handler class for server events */
|
||||||
class ServerCallbacks : public NimBLEServerCallbacks {
|
class ServerCallbacks : public NimBLEServerCallbacks {
|
||||||
void onConnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo) override {
|
void onConnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo) override {
|
||||||
printf("Client connected:: %s\n", connInfo.getAddress().toString().c_str());
|
printf("Client connected:\n%s", connInfo.toString().c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void onDisconnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo, int reason) override {
|
void onDisconnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo, int reason) override {
|
||||||
@@ -65,8 +65,7 @@ class AdvertisingCallbacks : public NimBLEExtAdvertisingCallbacks {
|
|||||||
}
|
}
|
||||||
} advertisingCallbacks;
|
} advertisingCallbacks;
|
||||||
|
|
||||||
extern "C"
|
extern "C" void app_main(void) {
|
||||||
void app_main(void) {
|
|
||||||
/** Initialize NimBLE and set the device name */
|
/** Initialize NimBLE and set the device name */
|
||||||
NimBLEDevice::init("Extended advertiser");
|
NimBLEDevice::init("Extended advertiser");
|
||||||
|
|
||||||
@@ -81,9 +80,6 @@ void app_main(void) {
|
|||||||
|
|
||||||
pCharacteristic->setValue("Hello World");
|
pCharacteristic->setValue("Hello World");
|
||||||
|
|
||||||
/** Start the service */
|
|
||||||
pService->start();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create an extended advertisement with the instance ID 0 and set the PHY's.
|
* Create an extended advertisement with the instance ID 0 and set the PHY's.
|
||||||
* Multiple instances can be added as long as the instance ID is incremented.
|
* Multiple instances can be added as long as the instance ID is incremented.
|
||||||
@@ -105,7 +101,6 @@ void app_main(void) {
|
|||||||
"This example message is 226 bytes long "
|
"This example message is 226 bytes long "
|
||||||
"and is using CODED_PHY for long range."));
|
"and is using CODED_PHY for long range."));
|
||||||
|
|
||||||
extAdv.setCompleteServices16({NimBLEUUID(SERVICE_UUID)});
|
|
||||||
extAdv.setName("Extended advertiser");
|
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 */
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ static uint8_t secondaryPhy = BLE_HCI_LE_PHY_1M;
|
|||||||
/** Handler class for server events */
|
/** Handler class for server events */
|
||||||
class ServerCallbacks : public NimBLEServerCallbacks {
|
class ServerCallbacks : public NimBLEServerCallbacks {
|
||||||
void onConnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo) override {
|
void onConnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo) override {
|
||||||
printf("Client connected: %s\n", connInfo.getAddress().toString().c_str());
|
printf("Client connected:\n%s", connInfo.toString().c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void onDisconnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo, int reason) override {
|
void onDisconnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo, int reason) override {
|
||||||
@@ -98,9 +98,6 @@ extern "C" void app_main(void) {
|
|||||||
|
|
||||||
pCharacteristic->setValue("Hello World");
|
pCharacteristic->setValue("Hello World");
|
||||||
|
|
||||||
/** 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. */
|
||||||
|
|||||||
5
examples/L2CAP/.gitignore
vendored
Normal file
5
examples/L2CAP/.gitignore
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
.vscode
|
||||||
|
build
|
||||||
|
sdkconfig
|
||||||
|
sdkconfig.old
|
||||||
|
dependencies.lock
|
||||||
7
examples/L2CAP/L2CAP_Client/CMakeLists.txt
Normal file
7
examples/L2CAP/L2CAP_Client/CMakeLists.txt
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
# The following lines of boilerplate have to be in your project's
|
||||||
|
# CMakeLists in this exact order for cmake to work correctly
|
||||||
|
cmake_minimum_required(VERSION 3.5)
|
||||||
|
|
||||||
|
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||||
|
set(SUPPORTED_TARGETS esp32 esp32s3 esp32c3 esp32c6)
|
||||||
|
project(L2CAP_client)
|
||||||
3
examples/L2CAP/L2CAP_Client/Makefile
Normal file
3
examples/L2CAP/L2CAP_Client/Makefile
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
PROJECT_NAME := L2CAP_client
|
||||||
|
|
||||||
|
include $(IDF_PATH)/make/project.mk
|
||||||
4
examples/L2CAP/L2CAP_Client/main/CMakeLists.txt
Normal file
4
examples/L2CAP/L2CAP_Client/main/CMakeLists.txt
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
set(COMPONENT_SRCS "main.cpp")
|
||||||
|
set(COMPONENT_ADD_INCLUDEDIRS ".")
|
||||||
|
|
||||||
|
register_component()
|
||||||
4
examples/L2CAP/L2CAP_Client/main/component.mk
Normal file
4
examples/L2CAP/L2CAP_Client/main/component.mk
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
#
|
||||||
|
# "main" pseudo-component makefile.
|
||||||
|
#
|
||||||
|
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)
|
||||||
3
examples/L2CAP/L2CAP_Client/main/idf_component.yml
Normal file
3
examples/L2CAP/L2CAP_Client/main/idf_component.yml
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
dependencies:
|
||||||
|
local/esp-nimble-cpp:
|
||||||
|
path: ../../../../../esp-nimble-cpp/
|
||||||
165
examples/L2CAP/L2CAP_Client/main/main.cpp
Normal file
165
examples/L2CAP/L2CAP_Client/main/main.cpp
Normal file
@@ -0,0 +1,165 @@
|
|||||||
|
#include <NimBLEDevice.h>
|
||||||
|
|
||||||
|
// See the following for generating UUIDs:
|
||||||
|
// https://www.uuidgenerator.net/
|
||||||
|
|
||||||
|
// The remote service we wish to connect to.
|
||||||
|
static BLEUUID serviceUUID("dcbc7255-1e9e-49a0-a360-b0430b6c6905");
|
||||||
|
// The characteristic of the remote service we are interested in.
|
||||||
|
static BLEUUID charUUID("371a55c8-f251-4ad2-90b3-c7c195b049be");
|
||||||
|
|
||||||
|
#define L2CAP_CHANNEL 150
|
||||||
|
#define L2CAP_MTU 5000
|
||||||
|
|
||||||
|
const BLEAdvertisedDevice* theDevice = NULL;
|
||||||
|
BLEClient* theClient = NULL;
|
||||||
|
BLEL2CAPChannel* theChannel = NULL;
|
||||||
|
|
||||||
|
size_t bytesSent = 0;
|
||||||
|
size_t bytesReceived = 0;
|
||||||
|
|
||||||
|
class L2CAPChannelCallbacks: public BLEL2CAPChannelCallbacks {
|
||||||
|
|
||||||
|
public:
|
||||||
|
void onConnect(NimBLEL2CAPChannel* channel) {
|
||||||
|
printf("L2CAP connection established\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void onMTUChange(NimBLEL2CAPChannel* channel, uint16_t mtu) {
|
||||||
|
printf("L2CAP MTU changed to %d\n", mtu);
|
||||||
|
}
|
||||||
|
|
||||||
|
void onRead(NimBLEL2CAPChannel* channel, std::vector<uint8_t>& data) {
|
||||||
|
printf("L2CAP read %d bytes\n", data.size());
|
||||||
|
}
|
||||||
|
void onDisconnect(NimBLEL2CAPChannel* channel) {
|
||||||
|
printf("L2CAP disconnected\n");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class MyClientCallbacks: public BLEClientCallbacks {
|
||||||
|
|
||||||
|
void onConnect(BLEClient* pClient) {
|
||||||
|
printf("GAP connected\n");
|
||||||
|
pClient->setDataLen(251);
|
||||||
|
|
||||||
|
theChannel = BLEL2CAPChannel::connect(pClient, L2CAP_CHANNEL, L2CAP_MTU, new L2CAPChannelCallbacks());
|
||||||
|
}
|
||||||
|
|
||||||
|
void onDisconnect(BLEClient* pClient, int reason) {
|
||||||
|
printf("GAP disconnected (reason: %d)\n", reason);
|
||||||
|
theDevice = NULL;
|
||||||
|
theChannel = NULL;
|
||||||
|
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||||
|
BLEDevice::getScan()->start(5 * 1000, true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks {
|
||||||
|
|
||||||
|
void onResult(const BLEAdvertisedDevice* advertisedDevice) {
|
||||||
|
if (theDevice) { return; }
|
||||||
|
printf("BLE Advertised Device found: %s\n", advertisedDevice->toString().c_str());
|
||||||
|
|
||||||
|
if (!advertisedDevice->haveServiceUUID()) { return; }
|
||||||
|
if (!advertisedDevice->isAdvertisingService(serviceUUID)) { return; }
|
||||||
|
|
||||||
|
printf("Found the device we're interested in!\n");
|
||||||
|
BLEDevice::getScan()->stop();
|
||||||
|
|
||||||
|
// Hand over the device to the other task
|
||||||
|
theDevice = advertisedDevice;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void connectTask(void *pvParameters) {
|
||||||
|
|
||||||
|
uint8_t sequenceNumber = 0;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
|
||||||
|
if (!theDevice) {
|
||||||
|
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!theClient) {
|
||||||
|
theClient = BLEDevice::createClient();
|
||||||
|
theClient->setConnectionParams(6, 6, 0, 42);
|
||||||
|
|
||||||
|
auto callbacks = new MyClientCallbacks();
|
||||||
|
theClient->setClientCallbacks(callbacks);
|
||||||
|
|
||||||
|
auto success = theClient->connect(theDevice);
|
||||||
|
if (!success) {
|
||||||
|
printf("Error: Could not connect to device\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
vTaskDelay(2000 / portTICK_PERIOD_MS);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!theChannel) {
|
||||||
|
printf("l2cap channel not initialized\n");
|
||||||
|
vTaskDelay(2000 / portTICK_PERIOD_MS);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!theChannel->isConnected()) {
|
||||||
|
printf("l2cap channel not connected\n");
|
||||||
|
vTaskDelay(2000 / portTICK_PERIOD_MS);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (theChannel->isConnected()) {
|
||||||
|
|
||||||
|
/*
|
||||||
|
static auto initialDelay = true;
|
||||||
|
if (initialDelay) {
|
||||||
|
printf("Waiting gracefully 3 seconds before sending data\n");
|
||||||
|
vTaskDelay(3000 / portTICK_PERIOD_MS);
|
||||||
|
initialDelay = false;
|
||||||
|
};
|
||||||
|
*/
|
||||||
|
std::vector<uint8_t> data(5000, sequenceNumber++);
|
||||||
|
if (theChannel->write(data)) {
|
||||||
|
bytesSent += data.size();
|
||||||
|
} else {
|
||||||
|
printf("failed to send!\n");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C"
|
||||||
|
void app_main(void) {
|
||||||
|
printf("Starting L2CAP client example\n");
|
||||||
|
|
||||||
|
xTaskCreate(connectTask, "connectTask", 5000, NULL, 1, NULL);
|
||||||
|
|
||||||
|
BLEDevice::init("L2CAP-Client");
|
||||||
|
BLEDevice::setMTU(BLE_ATT_MTU_MAX);
|
||||||
|
|
||||||
|
auto scan = BLEDevice::getScan();
|
||||||
|
auto callbacks = new MyAdvertisedDeviceCallbacks();
|
||||||
|
scan->setScanCallbacks(callbacks);
|
||||||
|
scan->setInterval(1349);
|
||||||
|
scan->setWindow(449);
|
||||||
|
scan->setActiveScan(true);
|
||||||
|
scan->start(25 * 1000, false);
|
||||||
|
|
||||||
|
int numberOfSeconds = 0;
|
||||||
|
|
||||||
|
while (bytesSent == 0) {
|
||||||
|
vTaskDelay(10 / portTICK_PERIOD_MS);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||||
|
int bytesSentPerSeconds = bytesSent / ++numberOfSeconds;
|
||||||
|
printf("Bandwidth: %d b/sec = %d KB/sec\n", bytesSentPerSeconds, bytesSentPerSeconds / 1024);
|
||||||
|
}
|
||||||
|
}
|
||||||
13
examples/L2CAP/L2CAP_Client/sdkconfig.defaults
Normal file
13
examples/L2CAP/L2CAP_Client/sdkconfig.defaults
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
# 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
|
||||||
|
CONFIG_BT_NIMBLE_L2CAP_COC_MAX_NUM=1
|
||||||
7
examples/L2CAP/L2CAP_Server/CMakeLists.txt
Normal file
7
examples/L2CAP/L2CAP_Server/CMakeLists.txt
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
# The following lines of boilerplate have to be in your project's
|
||||||
|
# CMakeLists in this exact order for cmake to work correctly
|
||||||
|
cmake_minimum_required(VERSION 3.5)
|
||||||
|
|
||||||
|
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||||
|
set(SUPPORTED_TARGETS esp32 esp32s3 esp32c3 esp32c6)
|
||||||
|
project(L2CAP_server)
|
||||||
3
examples/L2CAP/L2CAP_Server/Makefile
Normal file
3
examples/L2CAP/L2CAP_Server/Makefile
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
PROJECT_NAME := L2CAP_server
|
||||||
|
|
||||||
|
include $(IDF_PATH)/make/project.mk
|
||||||
4
examples/L2CAP/L2CAP_Server/main/CMakeLists.txt
Normal file
4
examples/L2CAP/L2CAP_Server/main/CMakeLists.txt
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
set(COMPONENT_SRCS "main.cpp")
|
||||||
|
set(COMPONENT_ADD_INCLUDEDIRS ".")
|
||||||
|
|
||||||
|
register_component()
|
||||||
4
examples/L2CAP/L2CAP_Server/main/component.mk
Normal file
4
examples/L2CAP/L2CAP_Server/main/component.mk
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
#
|
||||||
|
# "main" pseudo-component makefile.
|
||||||
|
#
|
||||||
|
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)
|
||||||
3
examples/L2CAP/L2CAP_Server/main/idf_component.yml
Normal file
3
examples/L2CAP/L2CAP_Server/main/idf_component.yml
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
dependencies:
|
||||||
|
local/esp-nimble-cpp:
|
||||||
|
path: ../../../../../esp-nimble-cpp/
|
||||||
90
examples/L2CAP/L2CAP_Server/main/main.cpp
Normal file
90
examples/L2CAP/L2CAP_Server/main/main.cpp
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
#include <NimBLEDevice.h>
|
||||||
|
|
||||||
|
// See the following for generating UUIDs:
|
||||||
|
// https://www.uuidgenerator.net/
|
||||||
|
|
||||||
|
#define SERVICE_UUID "dcbc7255-1e9e-49a0-a360-b0430b6c6905"
|
||||||
|
#define CHARACTERISTIC_UUID "371a55c8-f251-4ad2-90b3-c7c195b049be"
|
||||||
|
#define L2CAP_CHANNEL 150
|
||||||
|
#define L2CAP_MTU 5000
|
||||||
|
|
||||||
|
class GATTCallbacks: public BLEServerCallbacks {
|
||||||
|
|
||||||
|
public:
|
||||||
|
void onConnect(BLEServer* pServer, BLEConnInfo& info) {
|
||||||
|
/// Booster #1
|
||||||
|
pServer->setDataLen(info.getConnHandle(), 251);
|
||||||
|
/// Booster #2 (especially for Apple devices)
|
||||||
|
BLEDevice::getServer()->updateConnParams(info.getConnHandle(), 12, 12, 0, 200);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class L2CAPChannelCallbacks: public BLEL2CAPChannelCallbacks {
|
||||||
|
|
||||||
|
public:
|
||||||
|
bool connected = false;
|
||||||
|
size_t numberOfReceivedBytes;
|
||||||
|
uint8_t nextSequenceNumber;
|
||||||
|
|
||||||
|
public:
|
||||||
|
void onConnect(NimBLEL2CAPChannel* channel) {
|
||||||
|
printf("L2CAP connection established\n");
|
||||||
|
connected = true;
|
||||||
|
numberOfReceivedBytes = nextSequenceNumber = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void onRead(NimBLEL2CAPChannel* channel, std::vector<uint8_t>& data) {
|
||||||
|
numberOfReceivedBytes += data.size();
|
||||||
|
size_t sequenceNumber = data[0];
|
||||||
|
printf("L2CAP read %d bytes w/ sequence number %d", data.size(), sequenceNumber);
|
||||||
|
if (sequenceNumber != nextSequenceNumber) {
|
||||||
|
printf("(wrong sequence number %d, expected %d)\n", sequenceNumber, nextSequenceNumber);
|
||||||
|
} else {
|
||||||
|
printf("\n");
|
||||||
|
nextSequenceNumber++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void onDisconnect(NimBLEL2CAPChannel* channel) {
|
||||||
|
printf("L2CAP disconnected\n");
|
||||||
|
connected = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
extern "C"
|
||||||
|
void app_main(void) {
|
||||||
|
printf("Starting L2CAP server example [%lu free] [%lu min]\n", esp_get_free_heap_size(), esp_get_minimum_free_heap_size());
|
||||||
|
|
||||||
|
BLEDevice::init("L2CAP-Server");
|
||||||
|
BLEDevice::setMTU(BLE_ATT_MTU_MAX);
|
||||||
|
|
||||||
|
auto cocServer = BLEDevice::createL2CAPServer();
|
||||||
|
auto l2capChannelCallbacks = new L2CAPChannelCallbacks();
|
||||||
|
auto channel = cocServer->createService(L2CAP_CHANNEL, L2CAP_MTU, l2capChannelCallbacks);
|
||||||
|
|
||||||
|
auto server = BLEDevice::createServer();
|
||||||
|
server->setCallbacks(new GATTCallbacks());
|
||||||
|
auto service = server->createService(SERVICE_UUID);
|
||||||
|
auto characteristic = service->createCharacteristic(CHARACTERISTIC_UUID, NIMBLE_PROPERTY::READ);
|
||||||
|
characteristic->setValue(L2CAP_CHANNEL);
|
||||||
|
service->start();
|
||||||
|
auto advertising = BLEDevice::getAdvertising();
|
||||||
|
advertising->addServiceUUID(SERVICE_UUID);
|
||||||
|
advertising->enableScanResponse(true);
|
||||||
|
|
||||||
|
BLEDevice::startAdvertising();
|
||||||
|
printf("Server waiting for connection requests [%lu free] [%lu min]\n", esp_get_free_heap_size(), esp_get_minimum_free_heap_size());
|
||||||
|
|
||||||
|
// Wait until transfer actually starts...
|
||||||
|
while (!l2capChannelCallbacks->numberOfReceivedBytes) {
|
||||||
|
vTaskDelay(10 / portTICK_PERIOD_MS);
|
||||||
|
}
|
||||||
|
printf("\n\n\n");
|
||||||
|
int numberOfSeconds = 0;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||||
|
if (!l2capChannelCallbacks->connected) { continue; }
|
||||||
|
int bps = l2capChannelCallbacks->numberOfReceivedBytes / ++numberOfSeconds;
|
||||||
|
printf("Bandwidth: %d b/sec = %d KB/sec [%lu free] [%lu min]\n", bps, bps / 1024, esp_get_free_heap_size(), esp_get_minimum_free_heap_size());
|
||||||
|
}
|
||||||
|
}
|
||||||
13
examples/L2CAP/L2CAP_Server/sdkconfig.defaults
Normal file
13
examples/L2CAP/L2CAP_Server/sdkconfig.defaults
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
# 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
|
||||||
|
CONFIG_BT_NIMBLE_L2CAP_COC_MAX_NUM=1
|
||||||
@@ -112,7 +112,7 @@ bool connectToServer() {
|
|||||||
|
|
||||||
/** No client to reuse? Create a new one. */
|
/** No client to reuse? Create a new one. */
|
||||||
if (!pClient) {
|
if (!pClient) {
|
||||||
if (NimBLEDevice::getCreatedClientCount() >= NIMBLE_MAX_CONNECTIONS) {
|
if (NimBLEDevice::getCreatedClientCount() >= MYNEWT_VAL(BLE_MAX_CONNECTIONS)) {
|
||||||
printf("Max clients reached - no more connections available\n");
|
printf("Max clients reached - no more connections available\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -261,8 +261,7 @@ extern "C" void app_main(void) {
|
|||||||
* These are the default values, only shown here for demonstration.
|
* These are the default values, only shown here for demonstration.
|
||||||
*/
|
*/
|
||||||
// NimBLEDevice::setSecurityAuth(false, false, true);
|
// NimBLEDevice::setSecurityAuth(false, false, true);
|
||||||
|
// NimBLEDevice::setSecurityAuth(BLE_SM_PAIR_AUTHREQ_BOND | BLE_SM_PAIR_AUTHREQ_MITM | BLE_SM_PAIR_AUTHREQ_SC);
|
||||||
NimBLEDevice::setSecurityAuth(/*BLE_SM_PAIR_AUTHREQ_BOND | BLE_SM_PAIR_AUTHREQ_MITM |*/ BLE_SM_PAIR_AUTHREQ_SC);
|
|
||||||
|
|
||||||
/** Optional: set the transmit power */
|
/** Optional: set the transmit power */
|
||||||
NimBLEDevice::setPower(3); /** 3dbm */
|
NimBLEDevice::setPower(3); /** 3dbm */
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ static NimBLEServer* pServer;
|
|||||||
** Remove as you see fit for your needs */
|
** Remove as you see fit for your needs */
|
||||||
class ServerCallbacks : public NimBLEServerCallbacks {
|
class ServerCallbacks : public NimBLEServerCallbacks {
|
||||||
void onConnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo) override {
|
void onConnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo) override {
|
||||||
printf("Client address: %s\n", connInfo.getAddress().toString().c_str());
|
printf("Client connected:\n%s", connInfo.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.
|
||||||
@@ -26,7 +26,7 @@ class ServerCallbacks : public NimBLEServerCallbacks {
|
|||||||
* Latency: number of intervals allowed to skip.
|
* Latency: number of intervals allowed to skip.
|
||||||
* Timeout: 10 millisecond increments.
|
* 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) override {
|
void onDisconnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo, int reason) override {
|
||||||
@@ -142,8 +142,8 @@ extern "C" void app_main(void) {
|
|||||||
* These are the default values, only shown here for demonstration.
|
* These are the default values, only shown here for demonstration.
|
||||||
*/
|
*/
|
||||||
// NimBLEDevice::setSecurityAuth(false, false, true);
|
// NimBLEDevice::setSecurityAuth(false, false, true);
|
||||||
|
// NimBLEDevice::setSecurityAuth(BLE_SM_PAIR_AUTHREQ_BOND | BLE_SM_PAIR_AUTHREQ_MITM | BLE_SM_PAIR_AUTHREQ_SC);
|
||||||
|
|
||||||
NimBLEDevice::setSecurityAuth(/*BLE_SM_PAIR_AUTHREQ_BOND | BLE_SM_PAIR_AUTHREQ_MITM |*/ BLE_SM_PAIR_AUTHREQ_SC);
|
|
||||||
pServer = NimBLEDevice::createServer();
|
pServer = NimBLEDevice::createServer();
|
||||||
pServer->setCallbacks(&serverCallbacks);
|
pServer->setCallbacks(&serverCallbacks);
|
||||||
|
|
||||||
@@ -184,10 +184,6 @@ extern "C" void app_main(void) {
|
|||||||
pC01Ddsc->setValue("Send it back!");
|
pC01Ddsc->setValue("Send it back!");
|
||||||
pC01Ddsc->setCallbacks(&dscCallbacks);
|
pC01Ddsc->setCallbacks(&dscCallbacks);
|
||||||
|
|
||||||
/** Start the services when finished creating all Characteristics and Descriptors */
|
|
||||||
pDeadService->start();
|
|
||||||
pBaadService->start();
|
|
||||||
|
|
||||||
/** Create an advertising instance and add the services to the advertised data */
|
/** Create an advertising instance and add the services to the advertised data */
|
||||||
NimBLEAdvertising* pAdvertising = NimBLEDevice::getAdvertising();
|
NimBLEAdvertising* pAdvertising = NimBLEDevice::getAdvertising();
|
||||||
pAdvertising->setName("NimBLE-Server");
|
pAdvertising->setName("NimBLE-Server");
|
||||||
|
|||||||
6
examples/NimBLE_Stream_Client/CMakeLists.txt
Normal file
6
examples/NimBLE_Stream_Client/CMakeLists.txt
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
# 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)
|
||||||
|
project(NimBLE_Stream_Client)
|
||||||
53
examples/NimBLE_Stream_Client/README.md
Normal file
53
examples/NimBLE_Stream_Client/README.md
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
# NimBLE Stream Client Example
|
||||||
|
|
||||||
|
This example demonstrates how to use the `NimBLEStreamClient` class to connect to a BLE GATT server and communicate using the familiar Arduino Stream interface.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- Uses Arduino Stream interface (print, println, read, available, etc.)
|
||||||
|
- Automatic server discovery and connection
|
||||||
|
- Bidirectional communication
|
||||||
|
- Buffered TX/RX using ring buffers
|
||||||
|
- Automatic reconnection on disconnect
|
||||||
|
- Similar usage to Serial communication
|
||||||
|
|
||||||
|
## How it Works
|
||||||
|
|
||||||
|
1. Scans for BLE devices advertising the target service UUID
|
||||||
|
2. Connects to the server and discovers the stream characteristic
|
||||||
|
3. Initializes `NimBLEStreamClient` with the remote characteristic
|
||||||
|
4. Subscribes to notifications to receive data in the RX buffer
|
||||||
|
5. Uses familiar Stream methods like `print()`, `println()`, `read()`, and `available()`
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
1. Build and flash the NimBLE_Stream_Server example to one ESP32 using ESP-IDF (`idf.py build flash monitor`)
|
||||||
|
2. Build and flash this client example to another ESP32 using ESP-IDF
|
||||||
|
3. The client will automatically:
|
||||||
|
- Scan for the server
|
||||||
|
- Connect when found
|
||||||
|
- Set up the stream interface
|
||||||
|
- Begin bidirectional communication
|
||||||
|
4. Open `idf.py monitor` on each board to observe stream traffic
|
||||||
|
|
||||||
|
## Service UUIDs
|
||||||
|
|
||||||
|
Must match the server:
|
||||||
|
- Service: `6E400001-B5A3-F393-E0A9-E50E24DCCA9E`
|
||||||
|
- Characteristic: `6E400002-B5A3-F393-E0A9-E50E24DCCA9E`
|
||||||
|
|
||||||
|
## Monitor Output
|
||||||
|
|
||||||
|
The example displays:
|
||||||
|
- Server discovery progress
|
||||||
|
- Connection status
|
||||||
|
- All data received from the server
|
||||||
|
- Confirmation of data sent to the server
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
Run with NimBLE_Stream_Server to see bidirectional communication:
|
||||||
|
- Server sends periodic status messages
|
||||||
|
- Client sends periodic uptime messages
|
||||||
|
- Both echo data received from each other
|
||||||
|
- You can send data from either `idf.py monitor` session
|
||||||
4
examples/NimBLE_Stream_Client/main/CMakeLists.txt
Normal file
4
examples/NimBLE_Stream_Client/main/CMakeLists.txt
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
set(COMPONENT_SRCS "main.cpp")
|
||||||
|
set(COMPONENT_ADD_INCLUDEDIRS ".")
|
||||||
|
|
||||||
|
register_component()
|
||||||
217
examples/NimBLE_Stream_Client/main/main.cpp
Normal file
217
examples/NimBLE_Stream_Client/main/main.cpp
Normal file
@@ -0,0 +1,217 @@
|
|||||||
|
/**
|
||||||
|
* NimBLE_Stream_Client Example:
|
||||||
|
*
|
||||||
|
* Demonstrates using NimBLEStreamClient to connect to a BLE GATT server
|
||||||
|
* and communicate using the Stream-like interface.
|
||||||
|
*
|
||||||
|
* This example connects to the NimBLE_Stream_Server example.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "esp_timer.h"
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
|
|
||||||
|
#include <NimBLEDevice.h>
|
||||||
|
|
||||||
|
// Service and Characteristic UUIDs (must match the server)
|
||||||
|
#define SERVICE_UUID "6E400001-B5A3-F393-E0A9-E50E24DCCA9E"
|
||||||
|
#define CHARACTERISTIC_UUID "6E400002-B5A3-F393-E0A9-E50E24DCCA9E"
|
||||||
|
|
||||||
|
// Create the stream client instance
|
||||||
|
NimBLEStreamClient bleStream;
|
||||||
|
|
||||||
|
struct RxOverflowStats {
|
||||||
|
uint32_t droppedOld{0};
|
||||||
|
uint32_t droppedNew{0};
|
||||||
|
};
|
||||||
|
|
||||||
|
RxOverflowStats g_rxOverflowStats;
|
||||||
|
uint32_t scanTime = 5000; // Scan duration in milliseconds
|
||||||
|
|
||||||
|
NimBLEStream::RxOverflowAction onRxOverflow(const uint8_t* data, size_t len, void* userArg) {
|
||||||
|
auto* stats = static_cast<RxOverflowStats*>(userArg);
|
||||||
|
if (stats) {
|
||||||
|
stats->droppedOld++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For status/telemetry streams, prioritize newest packets.
|
||||||
|
(void)data;
|
||||||
|
(void)len;
|
||||||
|
return NimBLEStream::DROP_OLDER_DATA;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint64_t millis() {
|
||||||
|
return esp_timer_get_time() / 1000ULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Connection state variables
|
||||||
|
static bool doConnect = false;
|
||||||
|
static bool connected = false;
|
||||||
|
static const NimBLEAdvertisedDevice* pServerDevice = nullptr;
|
||||||
|
static NimBLEClient* pClient = nullptr;
|
||||||
|
|
||||||
|
/** Scan callbacks to find the server */
|
||||||
|
class ScanCallbacks : public NimBLEScanCallbacks {
|
||||||
|
void onResult(const NimBLEAdvertisedDevice* advertisedDevice) override {
|
||||||
|
printf("Advertised Device: %s\n", advertisedDevice->toString().c_str());
|
||||||
|
|
||||||
|
// Check if this device advertises our service.
|
||||||
|
if (advertisedDevice->isAdvertisingService(NimBLEUUID(SERVICE_UUID))) {
|
||||||
|
printf("Found our stream server!\n");
|
||||||
|
pServerDevice = advertisedDevice;
|
||||||
|
NimBLEDevice::getScan()->stop();
|
||||||
|
doConnect = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void onScanEnd(const NimBLEScanResults& results, int reason) override {
|
||||||
|
(void)results;
|
||||||
|
(void)reason;
|
||||||
|
printf("Scan ended\n");
|
||||||
|
if (!doConnect && !connected) {
|
||||||
|
printf("Server not found, restarting scan...\n");
|
||||||
|
NimBLEDevice::getScan()->start(scanTime, false, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} scanCallbacks;
|
||||||
|
|
||||||
|
/** Client callbacks for connection/disconnection events */
|
||||||
|
class ClientCallbacks : public NimBLEClientCallbacks {
|
||||||
|
void onConnect(NimBLEClient* pClient) override {
|
||||||
|
printf("Connected to server\n");
|
||||||
|
// Update connection parameters for better throughput.
|
||||||
|
pClient->updateConnParams(12, 24, 0, 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
void onDisconnect(NimBLEClient* pClient, int reason) override {
|
||||||
|
(void)pClient;
|
||||||
|
printf("Disconnected from server, reason: %d\n", reason);
|
||||||
|
connected = false;
|
||||||
|
bleStream.end();
|
||||||
|
|
||||||
|
// Restart scanning.
|
||||||
|
printf("Restarting scan...\n");
|
||||||
|
NimBLEDevice::getScan()->start(scanTime, false, true);
|
||||||
|
}
|
||||||
|
} clientCallbacks;
|
||||||
|
|
||||||
|
/** Connect to the BLE Server and set up the stream */
|
||||||
|
bool connectToServer() {
|
||||||
|
printf("Connecting to: %s\n", pServerDevice->getAddress().toString().c_str());
|
||||||
|
|
||||||
|
// Create or reuse a client.
|
||||||
|
pClient = NimBLEDevice::getClientByPeerAddress(pServerDevice->getAddress());
|
||||||
|
if (!pClient) {
|
||||||
|
pClient = NimBLEDevice::createClient();
|
||||||
|
if (!pClient) {
|
||||||
|
printf("Failed to create client\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
pClient->setClientCallbacks(&clientCallbacks, false);
|
||||||
|
pClient->setConnectionParams(12, 24, 0, 200);
|
||||||
|
pClient->setConnectTimeout(5000);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Connect to the remote BLE Server.
|
||||||
|
if (!pClient->connect(pServerDevice)) {
|
||||||
|
printf("Failed to connect to server\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Connected! Discovering services...\n");
|
||||||
|
|
||||||
|
// Get the service and characteristic.
|
||||||
|
NimBLERemoteService* pRemoteService = pClient->getService(SERVICE_UUID);
|
||||||
|
if (!pRemoteService) {
|
||||||
|
printf("Failed to find our service UUID\n");
|
||||||
|
pClient->disconnect();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
printf("Found the stream service\n");
|
||||||
|
|
||||||
|
NimBLERemoteCharacteristic* pRemoteCharacteristic = pRemoteService->getCharacteristic(CHARACTERISTIC_UUID);
|
||||||
|
if (!pRemoteCharacteristic) {
|
||||||
|
printf("Failed to find our characteristic UUID\n");
|
||||||
|
pClient->disconnect();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
printf("Found the stream characteristic\n");
|
||||||
|
|
||||||
|
// subscribeNotify=true means notifications are stored in the RX buffer.
|
||||||
|
if (!bleStream.begin(pRemoteCharacteristic, true)) {
|
||||||
|
printf("Failed to initialize BLE stream!\n");
|
||||||
|
pClient->disconnect();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bleStream.setRxOverflowCallback(onRxOverflow, &g_rxOverflowStats);
|
||||||
|
|
||||||
|
printf("BLE Stream initialized successfully!\n");
|
||||||
|
connected = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" void app_main(void) {
|
||||||
|
printf("Starting NimBLE Stream Client\n");
|
||||||
|
|
||||||
|
/** Initialize NimBLE */
|
||||||
|
NimBLEDevice::init("NimBLE-StreamClient");
|
||||||
|
|
||||||
|
// Create the BLE scan instance and set callbacks.
|
||||||
|
NimBLEScan* pScan = NimBLEDevice::getScan();
|
||||||
|
pScan->setScanCallbacks(&scanCallbacks, false);
|
||||||
|
pScan->setActiveScan(true);
|
||||||
|
|
||||||
|
// Start scanning for the server.
|
||||||
|
printf("Scanning for BLE Stream Server...\n");
|
||||||
|
pScan->start(scanTime, false, true);
|
||||||
|
|
||||||
|
uint32_t lastDroppedOld = 0;
|
||||||
|
uint32_t lastDroppedNew = 0;
|
||||||
|
uint64_t lastSend = 0;
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
if (g_rxOverflowStats.droppedOld != lastDroppedOld || g_rxOverflowStats.droppedNew != lastDroppedNew) {
|
||||||
|
lastDroppedOld = g_rxOverflowStats.droppedOld;
|
||||||
|
lastDroppedNew = g_rxOverflowStats.droppedNew;
|
||||||
|
printf("RX overflow handled (drop-old=%" PRIu32 ", drop-new=%" PRIu32 ")\n", lastDroppedOld, lastDroppedNew);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we found a server, try to connect.
|
||||||
|
if (doConnect) {
|
||||||
|
doConnect = false;
|
||||||
|
if (connectToServer()) {
|
||||||
|
printf("Stream ready for communication!\n");
|
||||||
|
} else {
|
||||||
|
printf("Failed to connect to server, restarting scan...\n");
|
||||||
|
pServerDevice = nullptr;
|
||||||
|
NimBLEDevice::getScan()->start(scanTime, false, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If connected, demonstrate stream communication.
|
||||||
|
if (connected && bleStream) {
|
||||||
|
if (bleStream.available()) {
|
||||||
|
printf("Received from server: ");
|
||||||
|
while (bleStream.available()) {
|
||||||
|
char c = bleStream.read();
|
||||||
|
putchar(c);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t now = millis();
|
||||||
|
if (now - lastSend > 5000) {
|
||||||
|
lastSend = now;
|
||||||
|
bleStream.printf("Hello from client! Uptime: %" PRIu64 " seconds\n", now / 1000);
|
||||||
|
printf("Sent data to server via BLE stream\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(10));
|
||||||
|
}
|
||||||
|
}
|
||||||
12
examples/NimBLE_Stream_Client/sdkconfig.defaults
Normal file
12
examples/NimBLE_Stream_Client/sdkconfig.defaults
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
# 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
|
||||||
6
examples/NimBLE_Stream_Echo/CMakeLists.txt
Normal file
6
examples/NimBLE_Stream_Echo/CMakeLists.txt
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
# 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)
|
||||||
|
project(NimBLE_Stream_Echo)
|
||||||
39
examples/NimBLE_Stream_Echo/README.md
Normal file
39
examples/NimBLE_Stream_Echo/README.md
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
# NimBLE Stream Echo Example
|
||||||
|
|
||||||
|
This is the simplest example demonstrating `NimBLEStreamServer`. It echoes back any data received from BLE clients.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- Minimal code showing essential NimBLE Stream usage
|
||||||
|
- Echoes all received data back to the client
|
||||||
|
- Uses default service and characteristic UUIDs
|
||||||
|
- Perfect starting point for learning the Stream interface
|
||||||
|
|
||||||
|
## How it Works
|
||||||
|
|
||||||
|
1. Initializes BLE with minimal configuration
|
||||||
|
2. Creates a stream server with default UUIDs
|
||||||
|
3. Waits for client connection and data
|
||||||
|
4. Echoes received data back to the client
|
||||||
|
5. Displays received data in the ESP-IDF monitor output
|
||||||
|
|
||||||
|
## Default UUIDs
|
||||||
|
|
||||||
|
- Service: `0xc0de`
|
||||||
|
- Characteristic: `0xfeed`
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
1. Build and flash this example to your ESP32 using ESP-IDF (`idf.py build flash monitor`)
|
||||||
|
2. Connect with a BLE client app (nRF Connect, Serial Bluetooth Terminal, etc.)
|
||||||
|
3. Find the service `0xc0de` and characteristic `0xfeed`
|
||||||
|
4. Subscribe to notifications
|
||||||
|
5. Write data to the characteristic
|
||||||
|
6. The data will be echoed back and displayed in `idf.py monitor`
|
||||||
|
|
||||||
|
## Good For
|
||||||
|
|
||||||
|
- Learning the basic NimBLE Stream API
|
||||||
|
- Testing BLE connectivity
|
||||||
|
- Starting point for custom applications
|
||||||
|
- Understanding Stream read/write operations
|
||||||
4
examples/NimBLE_Stream_Echo/main/CMakeLists.txt
Normal file
4
examples/NimBLE_Stream_Echo/main/CMakeLists.txt
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
set(COMPONENT_SRCS "main.cpp")
|
||||||
|
set(COMPONENT_ADD_INCLUDEDIRS ".")
|
||||||
|
|
||||||
|
register_component()
|
||||||
83
examples/NimBLE_Stream_Echo/main/main.cpp
Normal file
83
examples/NimBLE_Stream_Echo/main/main.cpp
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
/**
|
||||||
|
* NimBLE_Stream_Echo Example:
|
||||||
|
*
|
||||||
|
* A minimal example demonstrating NimBLEStreamServer.
|
||||||
|
* Echoes back any data received from BLE clients.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
|
|
||||||
|
#include <NimBLEDevice.h>
|
||||||
|
|
||||||
|
NimBLEStreamServer bleStream;
|
||||||
|
|
||||||
|
struct RxOverflowStats {
|
||||||
|
uint32_t droppedOld{0};
|
||||||
|
uint32_t droppedNew{0};
|
||||||
|
};
|
||||||
|
|
||||||
|
RxOverflowStats g_rxOverflowStats;
|
||||||
|
|
||||||
|
NimBLEStream::RxOverflowAction onRxOverflow(const uint8_t* data, size_t len, void* userArg) {
|
||||||
|
auto* stats = static_cast<RxOverflowStats*>(userArg);
|
||||||
|
if (stats) {
|
||||||
|
stats->droppedOld++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Echo mode prefers the latest incoming bytes.
|
||||||
|
(void)data;
|
||||||
|
(void)len;
|
||||||
|
return NimBLEStream::DROP_OLDER_DATA;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" void app_main(void) {
|
||||||
|
printf("NimBLE Stream Echo Server\n");
|
||||||
|
|
||||||
|
// Initialize BLE.
|
||||||
|
NimBLEDevice::init("BLE-Echo");
|
||||||
|
auto pServer = NimBLEDevice::createServer();
|
||||||
|
pServer->advertiseOnDisconnect(true); // Keep advertising after disconnects.
|
||||||
|
|
||||||
|
if (!bleStream.begin(NimBLEUUID(uint16_t(0xc0de)),
|
||||||
|
NimBLEUUID(uint16_t(0xfeed)),
|
||||||
|
1024,
|
||||||
|
1024,
|
||||||
|
false)) {
|
||||||
|
printf("Failed to initialize BLE stream\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bleStream.setRxOverflowCallback(onRxOverflow, &g_rxOverflowStats);
|
||||||
|
|
||||||
|
// Start advertising.
|
||||||
|
NimBLEDevice::getAdvertising()->start();
|
||||||
|
printf("Ready! Connect with a BLE client and send data.\n");
|
||||||
|
|
||||||
|
uint32_t lastDroppedOld = 0;
|
||||||
|
uint32_t lastDroppedNew = 0;
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
if (g_rxOverflowStats.droppedOld != lastDroppedOld || g_rxOverflowStats.droppedNew != lastDroppedNew) {
|
||||||
|
lastDroppedOld = g_rxOverflowStats.droppedOld;
|
||||||
|
lastDroppedNew = g_rxOverflowStats.droppedNew;
|
||||||
|
printf("RX overflow handled (drop-old=%" PRIu32 ", drop-new=%" PRIu32 ")\n", lastDroppedOld, lastDroppedNew);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Echo any received data back to the client.
|
||||||
|
if (bleStream.ready() && bleStream.available()) {
|
||||||
|
printf("Echo: ");
|
||||||
|
while (bleStream.available()) {
|
||||||
|
char c = bleStream.read();
|
||||||
|
putchar(c);
|
||||||
|
bleStream.write(c);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(10));
|
||||||
|
}
|
||||||
|
}
|
||||||
12
examples/NimBLE_Stream_Echo/sdkconfig.defaults
Normal file
12
examples/NimBLE_Stream_Echo/sdkconfig.defaults
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
# 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
|
||||||
6
examples/NimBLE_Stream_Server/CMakeLists.txt
Normal file
6
examples/NimBLE_Stream_Server/CMakeLists.txt
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
# 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)
|
||||||
|
project(NimBLE_Stream_Server)
|
||||||
42
examples/NimBLE_Stream_Server/README.md
Normal file
42
examples/NimBLE_Stream_Server/README.md
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
# NimBLE Stream Server Example
|
||||||
|
|
||||||
|
This example demonstrates how to use the `NimBLEStreamServer` class to create a BLE GATT server that behaves like a serial port using the familiar Arduino Stream interface.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- Uses Arduino Stream interface (print, println, read, available, etc.)
|
||||||
|
- Automatic connection management
|
||||||
|
- Bidirectional communication
|
||||||
|
- Buffered TX/RX using ring buffers
|
||||||
|
- Similar usage to Serial communication
|
||||||
|
|
||||||
|
## How it Works
|
||||||
|
|
||||||
|
1. Creates a BLE GATT server with a custom service and characteristic
|
||||||
|
2. Initializes `NimBLEStreamServer` with the characteristic configured for notifications and writes
|
||||||
|
3. Uses familiar Stream methods like `print()`, `println()`, `read()`, and `available()`
|
||||||
|
4. Automatically handles connection state and MTU negotiation
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
1. Build and flash this example to your ESP32 using ESP-IDF (`idf.py build flash monitor`)
|
||||||
|
2. The device will advertise as "NimBLE-Stream"
|
||||||
|
3. Connect with a BLE client (such as the NimBLE_Stream_Client example or a mobile app)
|
||||||
|
4. Once connected, the server will:
|
||||||
|
- Send periodic messages to the client
|
||||||
|
- Echo back any data received from the client
|
||||||
|
- Display all communication in `idf.py monitor`
|
||||||
|
|
||||||
|
## Service UUIDs
|
||||||
|
|
||||||
|
- Service: `6E400001-B5A3-F393-E0A9-E50E24DCCA9E`
|
||||||
|
- Characteristic: `6E400002-B5A3-F393-E0A9-E50E24DCCA9E`
|
||||||
|
|
||||||
|
These are based on the Nordic UART Service (NUS) UUIDs for compatibility with many BLE terminal apps.
|
||||||
|
|
||||||
|
## Compatible With
|
||||||
|
|
||||||
|
- NimBLE_Stream_Client example
|
||||||
|
- nRF Connect mobile app
|
||||||
|
- Serial Bluetooth Terminal apps
|
||||||
|
- Any BLE client that supports characteristic notifications and writes
|
||||||
4
examples/NimBLE_Stream_Server/main/CMakeLists.txt
Normal file
4
examples/NimBLE_Stream_Server/main/CMakeLists.txt
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
set(COMPONENT_SRCS "main.cpp")
|
||||||
|
set(COMPONENT_ADD_INCLUDEDIRS ".")
|
||||||
|
|
||||||
|
register_component()
|
||||||
146
examples/NimBLE_Stream_Server/main/main.cpp
Normal file
146
examples/NimBLE_Stream_Server/main/main.cpp
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
/**
|
||||||
|
* NimBLE_Stream_Server Example:
|
||||||
|
*
|
||||||
|
* Demonstrates using NimBLEStreamServer to create a BLE GATT server
|
||||||
|
* that behaves like a serial port using the Stream-like interface.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "esp_heap_caps.h"
|
||||||
|
#include "esp_timer.h"
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
|
|
||||||
|
#include <NimBLEDevice.h>
|
||||||
|
|
||||||
|
// Create the stream server instance
|
||||||
|
NimBLEStreamServer bleStream;
|
||||||
|
|
||||||
|
struct RxOverflowStats {
|
||||||
|
uint32_t droppedOld{0};
|
||||||
|
uint32_t droppedNew{0};
|
||||||
|
};
|
||||||
|
|
||||||
|
RxOverflowStats g_rxOverflowStats;
|
||||||
|
|
||||||
|
NimBLEStream::RxOverflowAction onRxOverflow(const uint8_t* data, size_t len, void* userArg) {
|
||||||
|
auto* stats = static_cast<RxOverflowStats*>(userArg);
|
||||||
|
if (stats) {
|
||||||
|
stats->droppedOld++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep the newest bytes for command/stream style traffic.
|
||||||
|
(void)data;
|
||||||
|
(void)len;
|
||||||
|
return NimBLEStream::DROP_OLDER_DATA;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint64_t millis() {
|
||||||
|
return esp_timer_get_time() / 1000ULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Service and Characteristic UUIDs for the stream.
|
||||||
|
#define SERVICE_UUID "6E400001-B5A3-F393-E0A9-E50E24DCCA9E"
|
||||||
|
#define CHARACTERISTIC_UUID "6E400002-B5A3-F393-E0A9-E50E24DCCA9E"
|
||||||
|
|
||||||
|
/** Server callbacks to handle connection/disconnection events */
|
||||||
|
class ServerCallbacks : public NimBLEServerCallbacks {
|
||||||
|
void onConnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo) override {
|
||||||
|
printf("Client connected: %s\n", connInfo.getAddress().toString().c_str());
|
||||||
|
// Optionally update connection parameters for better throughput.
|
||||||
|
pServer->updateConnParams(connInfo.getConnHandle(), 12, 24, 0, 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
void onDisconnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo, int reason) override {
|
||||||
|
(void)pServer;
|
||||||
|
(void)connInfo;
|
||||||
|
printf("Client disconnected - reason: %d, restarting advertising\n", reason);
|
||||||
|
NimBLEDevice::startAdvertising();
|
||||||
|
}
|
||||||
|
|
||||||
|
void onMTUChange(uint16_t MTU, NimBLEConnInfo& connInfo) override {
|
||||||
|
printf("MTU updated: %u for connection ID: %u\n", MTU, connInfo.getConnHandle());
|
||||||
|
}
|
||||||
|
} serverCallbacks;
|
||||||
|
|
||||||
|
extern "C" void app_main(void) {
|
||||||
|
printf("Starting NimBLE Stream Server\n");
|
||||||
|
|
||||||
|
/** Initialize NimBLE and set the device name */
|
||||||
|
NimBLEDevice::init("NimBLE-Stream");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create the BLE server and set callbacks.
|
||||||
|
* Note: The stream will create its own service and characteristic.
|
||||||
|
*/
|
||||||
|
NimBLEServer* pServer = NimBLEDevice::createServer();
|
||||||
|
pServer->setCallbacks(&serverCallbacks);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the stream server with:
|
||||||
|
* - Service UUID
|
||||||
|
* - Characteristic UUID
|
||||||
|
* - txBufSize: 1024 bytes for outgoing data (notifications)
|
||||||
|
* - rxBufSize: 1024 bytes for incoming data (writes)
|
||||||
|
* - secure: false (no encryption required - set true for secure connections)
|
||||||
|
*/
|
||||||
|
if (!bleStream.begin(NimBLEUUID(SERVICE_UUID),
|
||||||
|
NimBLEUUID(CHARACTERISTIC_UUID),
|
||||||
|
1024,
|
||||||
|
1024,
|
||||||
|
false)) {
|
||||||
|
printf("Failed to initialize BLE stream!\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bleStream.setRxOverflowCallback(onRxOverflow, &g_rxOverflowStats);
|
||||||
|
|
||||||
|
// Make the stream service discoverable.
|
||||||
|
NimBLEAdvertising* pAdvertising = NimBLEDevice::getAdvertising();
|
||||||
|
pAdvertising->addServiceUUID(SERVICE_UUID);
|
||||||
|
pAdvertising->setName("NimBLE-Stream");
|
||||||
|
pAdvertising->enableScanResponse(true);
|
||||||
|
pAdvertising->start();
|
||||||
|
|
||||||
|
printf("BLE Stream Server ready!\n");
|
||||||
|
printf("Waiting for client connection...\n");
|
||||||
|
|
||||||
|
uint32_t lastDroppedOld = 0;
|
||||||
|
uint32_t lastDroppedNew = 0;
|
||||||
|
uint64_t lastSend = 0;
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
if (g_rxOverflowStats.droppedOld != lastDroppedOld || g_rxOverflowStats.droppedNew != lastDroppedNew) {
|
||||||
|
lastDroppedOld = g_rxOverflowStats.droppedOld;
|
||||||
|
lastDroppedNew = g_rxOverflowStats.droppedNew;
|
||||||
|
printf("RX overflow handled (drop-old=%" PRIu32 ", drop-new=%" PRIu32 ")\n", lastDroppedOld, lastDroppedNew);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bleStream.ready()) {
|
||||||
|
uint64_t now = millis();
|
||||||
|
if (now - lastSend > 2000) {
|
||||||
|
lastSend = now;
|
||||||
|
bleStream.printf("Hello from server! Uptime: %" PRIu64 " seconds\n", now / 1000);
|
||||||
|
bleStream.printf("Free heap: %" PRIu32 " bytes\n", esp_get_free_heap_size());
|
||||||
|
printf("Sent data to client via BLE stream\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bleStream.available()) {
|
||||||
|
printf("Received from client: ");
|
||||||
|
while (bleStream.available()) {
|
||||||
|
char c = bleStream.read();
|
||||||
|
putchar(c);
|
||||||
|
bleStream.write(c); // Echo back to BLE client.
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(100));
|
||||||
|
}
|
||||||
|
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(10));
|
||||||
|
}
|
||||||
|
}
|
||||||
12
examples/NimBLE_Stream_Server/sdkconfig.defaults
Normal file
12
examples/NimBLE_Stream_Server/sdkconfig.defaults
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
# 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
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
## IDF Component Manager Manifest File
|
## IDF Component Manager Manifest File
|
||||||
version: "2.0.0"
|
version: "2.4.0"
|
||||||
license: "Apache-2.0"
|
license: "Apache-2.0"
|
||||||
description: "C++ wrapper for the NimBLE BLE stack"
|
description: "C++ wrapper for the NimBLE BLE stack"
|
||||||
url: "https://github.com/h2zero/esp-nimble-cpp"
|
url: "https://github.com/h2zero/esp-nimble-cpp"
|
||||||
@@ -16,7 +16,7 @@ dependencies:
|
|||||||
rules:
|
rules:
|
||||||
- if: "target in [esp32p4]"
|
- if: "target in [esp32p4]"
|
||||||
espressif/esp_wifi_remote:
|
espressif/esp_wifi_remote:
|
||||||
version: "*"
|
version: ">=0.5.3"
|
||||||
rules:
|
rules:
|
||||||
- if: "target in [esp32p4]"
|
- if: "target in [esp32p4]"
|
||||||
idf:
|
idf:
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "esp-nimble-cpp",
|
"name": "esp-nimble-cpp",
|
||||||
"version": "2.0.0",
|
"version": "2.4.0",
|
||||||
"description": "C++ wrapper for the NimBLE BLE stack",
|
"description": "C++ wrapper for the NimBLE BLE stack",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"BLE",
|
"BLE",
|
||||||
@@ -19,5 +19,10 @@
|
|||||||
"email": "ryan@nable-embedded.io",
|
"email": "ryan@nable-embedded.io",
|
||||||
"url": "https://github.com/h2zero/esp-nimble-cpp",
|
"url": "https://github.com/h2zero/esp-nimble-cpp",
|
||||||
"maintainer": true
|
"maintainer": true
|
||||||
|
},
|
||||||
|
"build": {
|
||||||
|
"flags": [
|
||||||
|
"-DCONFIG_NIMBLE_CPP_IDF=1"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
|
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
|
||||||
* esp-nimble-cpp, NimBLE-Arduino contributors.
|
* esp-nimble-cpp, NimBLE-Arduino contributors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@@ -15,10 +15,8 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "nimconfig.h"
|
#include "NimBLE2904.h"
|
||||||
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
|
#if CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_PERIPHERAL)
|
||||||
|
|
||||||
# include "NimBLE2904.h"
|
|
||||||
|
|
||||||
NimBLE2904::NimBLE2904(NimBLECharacteristic* pChr)
|
NimBLE2904::NimBLE2904(NimBLECharacteristic* pChr)
|
||||||
: NimBLEDescriptor(NimBLEUUID((uint16_t)0x2904), BLE_GATT_CHR_F_READ, sizeof(NimBLE2904Data), pChr) {
|
: NimBLEDescriptor(NimBLEUUID((uint16_t)0x2904), BLE_GATT_CHR_F_READ, sizeof(NimBLE2904Data), pChr) {
|
||||||
@@ -71,4 +69,4 @@ void NimBLE2904::setUnit(uint16_t unit) {
|
|||||||
setValue(m_data);
|
setValue(m_data);
|
||||||
} // setUnit
|
} // setUnit
|
||||||
|
|
||||||
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL */
|
#endif // CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_PERIPHERAL)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
|
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
|
||||||
* esp-nimble-cpp, NimBLE-Arduino contributors.
|
* esp-nimble-cpp, NimBLE-Arduino contributors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@@ -18,8 +18,8 @@
|
|||||||
#ifndef NIMBLE_CPP_2904_H_
|
#ifndef NIMBLE_CPP_2904_H_
|
||||||
#define NIMBLE_CPP_2904_H_
|
#define NIMBLE_CPP_2904_H_
|
||||||
|
|
||||||
#include "nimconfig.h"
|
#include "syscfg/syscfg.h"
|
||||||
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
|
#if CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_PERIPHERAL)
|
||||||
|
|
||||||
# include "NimBLEDescriptor.h"
|
# include "NimBLEDescriptor.h"
|
||||||
|
|
||||||
@@ -68,7 +68,7 @@ class NimBLE2904 : public NimBLEDescriptor {
|
|||||||
static const uint8_t FORMAT_OPAQUE = 27;
|
static const uint8_t FORMAT_OPAQUE = 27;
|
||||||
static const uint8_t FORMAT_MEDASN1 = 28;
|
static const uint8_t FORMAT_MEDASN1 = 28;
|
||||||
|
|
||||||
void setDescription(uint16_t);
|
void setDescription(uint16_t description);
|
||||||
void setExponent(int8_t exponent);
|
void setExponent(int8_t exponent);
|
||||||
void setFormat(uint8_t format);
|
void setFormat(uint8_t format);
|
||||||
void setNamespace(uint8_t namespace_value);
|
void setNamespace(uint8_t namespace_value);
|
||||||
@@ -79,5 +79,5 @@ class NimBLE2904 : public NimBLEDescriptor {
|
|||||||
NimBLE2904Data m_data{};
|
NimBLE2904Data m_data{};
|
||||||
}; // NimBLE2904
|
}; // NimBLE2904
|
||||||
|
|
||||||
#endif // CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL
|
#endif // CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_PERIPHERAL)
|
||||||
#endif // NIMBLE_CPP_2904_H_
|
#endif // NIMBLE_CPP_2904_H_
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
|
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
|
||||||
* esp-nimble-cpp, NimBLE-Arduino contributors.
|
* esp-nimble-cpp, NimBLE-Arduino contributors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@@ -15,14 +15,41 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "nimconfig.h"
|
#include "NimBLEAddress.h"
|
||||||
#if defined(CONFIG_BT_ENABLED)
|
#if CONFIG_BT_NIMBLE_ENABLED
|
||||||
|
|
||||||
# include "NimBLEAddress.h"
|
|
||||||
# include "NimBLELog.h"
|
# include "NimBLELog.h"
|
||||||
|
|
||||||
# include <algorithm>
|
# include <algorithm>
|
||||||
|
|
||||||
|
# ifndef MYNEWT_VAL_NIMBLE_CPP_ADDR_FMT_EXCLUDE_DELIMITER
|
||||||
|
# ifndef CONFIG_NIMBLE_CPP_ADDR_FMT_EXCLUDE_DELIMITER
|
||||||
|
# define MYNEWT_VAL_NIMBLE_CPP_ADDR_FMT_EXCLUDE_DELIMITER (0)
|
||||||
|
# else
|
||||||
|
# define MYNEWT_VAL_NIMBLE_CPP_ADDR_FMT_EXCLUDE_DELIMITER CONFIG_NIMBLE_CPP_ADDR_FMT_EXCLUDE_DELIMITER
|
||||||
|
# endif
|
||||||
|
# endif
|
||||||
|
|
||||||
|
# ifndef MYNEWT_VAL_NIMBLE_CPP_ADDR_FMT_UPPERCASE
|
||||||
|
# ifndef CONFIG_NIMBLE_CPP_ADDR_FMT_UPPERCASE
|
||||||
|
# define MYNEWT_VAL_NIMBLE_CPP_ADDR_FMT_UPPERCASE (1)
|
||||||
|
# else
|
||||||
|
# define MYNEWT_VAL_NIMBLE_CPP_ADDR_FMT_UPPERCASE CONFIG_NIMBLE_CPP_ADDR_FMT_UPPERCASE
|
||||||
|
# endif
|
||||||
|
# endif
|
||||||
|
|
||||||
|
# if MYNEWT_VAL(NIMBLE_CPP_ADDR_FMT_EXCLUDE_DELIMITER)
|
||||||
|
# define NIMBLE_CPP_ADDR_DELIMITER ""
|
||||||
|
# else
|
||||||
|
# define NIMBLE_CPP_ADDR_DELIMITER ":"
|
||||||
|
# endif
|
||||||
|
|
||||||
|
# if MYNEWT_VAL(NIMBLE_CPP_ADDR_FMT_UPPERCASE)
|
||||||
|
# define NIMBLE_CPP_ADDR_FMT "%02X%s%02X%s%02X%s%02X%s%02X%s%02X"
|
||||||
|
# else
|
||||||
|
# define NIMBLE_CPP_ADDR_FMT "%02x%s%02x%s%02x%s%02x%s%02x%s%02x"
|
||||||
|
# endif
|
||||||
|
|
||||||
static const char* LOG_TAG = "NimBLEAddress";
|
static const char* LOG_TAG = "NimBLEAddress";
|
||||||
|
|
||||||
/*************************************************
|
/*************************************************
|
||||||
@@ -61,7 +88,7 @@ NimBLEAddress::NimBLEAddress(const std::string& addr, uint8_t type) {
|
|||||||
std::string mac{addr};
|
std::string mac{addr};
|
||||||
mac.erase(std::remove(mac.begin(), mac.end(), ':'), mac.end());
|
mac.erase(std::remove(mac.begin(), mac.end(), ':'), mac.end());
|
||||||
uint64_t address = std::stoull(mac, nullptr, 16);
|
uint64_t address = std::stoull(mac, nullptr, 16);
|
||||||
memcpy(this->val, &address, sizeof this->val);
|
memcpy(this->val, &address, sizeof(this->val));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,7 +117,7 @@ NimBLEAddress::NimBLEAddress(const uint8_t address[BLE_DEV_ADDR_LEN], uint8_t ty
|
|||||||
* * BLE_ADDR_RANDOM (1)
|
* * BLE_ADDR_RANDOM (1)
|
||||||
*/
|
*/
|
||||||
NimBLEAddress::NimBLEAddress(const uint64_t& address, uint8_t type) {
|
NimBLEAddress::NimBLEAddress(const uint64_t& address, uint8_t type) {
|
||||||
memcpy(this->val, &address, sizeof this->val);
|
memcpy(this->val, &address, sizeof(this->val));
|
||||||
this->type = type;
|
this->type = type;
|
||||||
} // NimBLEAddress
|
} // NimBLEAddress
|
||||||
|
|
||||||
@@ -193,7 +220,7 @@ bool NimBLEAddress::operator==(const NimBLEAddress& rhs) const {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return memcmp(rhs.val, this->val, sizeof this->val) == 0;
|
return memcmp(rhs.val, this->val, sizeof(this->val)) == 0;
|
||||||
} // operator ==
|
} // operator ==
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -211,12 +238,17 @@ NimBLEAddress::operator std::string() const {
|
|||||||
char buffer[18];
|
char buffer[18];
|
||||||
snprintf(buffer,
|
snprintf(buffer,
|
||||||
sizeof(buffer),
|
sizeof(buffer),
|
||||||
"%02x:%02x:%02x:%02x:%02x:%02x",
|
NIMBLE_CPP_ADDR_FMT,
|
||||||
this->val[5],
|
this->val[5],
|
||||||
|
NIMBLE_CPP_ADDR_DELIMITER,
|
||||||
this->val[4],
|
this->val[4],
|
||||||
|
NIMBLE_CPP_ADDR_DELIMITER,
|
||||||
this->val[3],
|
this->val[3],
|
||||||
|
NIMBLE_CPP_ADDR_DELIMITER,
|
||||||
this->val[2],
|
this->val[2],
|
||||||
|
NIMBLE_CPP_ADDR_DELIMITER,
|
||||||
this->val[1],
|
this->val[1],
|
||||||
|
NIMBLE_CPP_ADDR_DELIMITER,
|
||||||
this->val[0]);
|
this->val[0]);
|
||||||
return std::string{buffer};
|
return std::string{buffer};
|
||||||
} // operator std::string
|
} // operator std::string
|
||||||
@@ -226,7 +258,7 @@ NimBLEAddress::operator std::string() const {
|
|||||||
*/
|
*/
|
||||||
NimBLEAddress::operator uint64_t() const {
|
NimBLEAddress::operator uint64_t() const {
|
||||||
uint64_t address = 0;
|
uint64_t address = 0;
|
||||||
memcpy(&address, this->val, sizeof this->val);
|
memcpy(&address, this->val, sizeof(this->val));
|
||||||
return address;
|
return address;
|
||||||
} // operator uint64_t
|
} // operator uint64_t
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
|
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
|
||||||
* esp-nimble-cpp, NimBLE-Arduino contributors.
|
* esp-nimble-cpp, NimBLE-Arduino contributors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@@ -17,8 +17,9 @@
|
|||||||
|
|
||||||
#ifndef NIMBLE_CPP_ADDRESS_H_
|
#ifndef NIMBLE_CPP_ADDRESS_H_
|
||||||
#define NIMBLE_CPP_ADDRESS_H_
|
#define NIMBLE_CPP_ADDRESS_H_
|
||||||
#include "nimconfig.h"
|
|
||||||
#if defined(CONFIG_BT_ENABLED)
|
#include "syscfg/syscfg.h"
|
||||||
|
#if CONFIG_BT_NIMBLE_ENABLED
|
||||||
|
|
||||||
# if defined(CONFIG_NIMBLE_CPP_IDF)
|
# if defined(CONFIG_NIMBLE_CPP_IDF)
|
||||||
# include "nimble/ble.h"
|
# include "nimble/ble.h"
|
||||||
@@ -66,5 +67,5 @@ class NimBLEAddress : private ble_addr_t {
|
|||||||
operator uint64_t() const;
|
operator uint64_t() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* CONFIG_BT_ENABLED */
|
#endif // CONFIG_BT_NIMBLE_ENABLED
|
||||||
#endif /* NIMBLE_CPP_ADDRESS_H_ */
|
#endif // NIMBLE_CPP_ADDRESS_H_
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
|
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
|
||||||
* esp-nimble-cpp, NimBLE-Arduino contributors.
|
* esp-nimble-cpp, NimBLE-Arduino contributors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@@ -15,11 +15,10 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "nimconfig.h"
|
#include "NimBLEAdvertisedDevice.h"
|
||||||
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
|
#if CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_OBSERVER)
|
||||||
|
|
||||||
# include "NimBLEDevice.h"
|
# include "NimBLEDevice.h"
|
||||||
# include "NimBLEAdvertisedDevice.h"
|
|
||||||
# include "NimBLEUtils.h"
|
# include "NimBLEUtils.h"
|
||||||
# include "NimBLELog.h"
|
# include "NimBLELog.h"
|
||||||
|
|
||||||
@@ -32,13 +31,14 @@ static const char* LOG_TAG = "NimBLEAdvertisedDevice";
|
|||||||
* @param [in] event The advertisement event data.
|
* @param [in] event The advertisement event data.
|
||||||
*/
|
*/
|
||||||
NimBLEAdvertisedDevice::NimBLEAdvertisedDevice(const ble_gap_event* event, uint8_t eventType)
|
NimBLEAdvertisedDevice::NimBLEAdvertisedDevice(const ble_gap_event* event, uint8_t eventType)
|
||||||
# if CONFIG_BT_NIMBLE_EXT_ADV
|
# if MYNEWT_VAL(BLE_EXT_ADV)
|
||||||
: m_address{event->ext_disc.addr},
|
: m_address{event->ext_disc.addr},
|
||||||
m_advType{eventType},
|
m_advType{eventType},
|
||||||
m_rssi{event->ext_disc.rssi},
|
m_rssi{event->ext_disc.rssi},
|
||||||
m_callbackSent{0},
|
m_callbackSent{0},
|
||||||
m_advLength{event->ext_disc.length_data},
|
m_advLength{event->ext_disc.length_data},
|
||||||
m_isLegacyAdv{!!(event->ext_disc.props & BLE_HCI_ADV_LEGACY_MASK)},
|
m_isLegacyAdv{!!(event->ext_disc.props & BLE_HCI_ADV_LEGACY_MASK)},
|
||||||
|
m_dataStatus{event->ext_disc.data_status},
|
||||||
m_sid{event->ext_disc.sid},
|
m_sid{event->ext_disc.sid},
|
||||||
m_primPhy{event->ext_disc.prim_phy},
|
m_primPhy{event->ext_disc.prim_phy},
|
||||||
m_secPhy{event->ext_disc.sec_phy},
|
m_secPhy{event->ext_disc.sec_phy},
|
||||||
@@ -59,9 +59,18 @@ NimBLEAdvertisedDevice::NimBLEAdvertisedDevice(const ble_gap_event* event, uint8
|
|||||||
* @param [in] event The advertisement event data.
|
* @param [in] event The advertisement event data.
|
||||||
*/
|
*/
|
||||||
void NimBLEAdvertisedDevice::update(const ble_gap_event* event, uint8_t eventType) {
|
void NimBLEAdvertisedDevice::update(const ble_gap_event* event, uint8_t eventType) {
|
||||||
# if CONFIG_BT_NIMBLE_EXT_ADV
|
# if MYNEWT_VAL(BLE_EXT_ADV)
|
||||||
const auto& disc = event->ext_disc;
|
const auto& disc = event->ext_disc;
|
||||||
m_isLegacyAdv = disc.props & BLE_HCI_ADV_LEGACY_MASK;
|
if (m_dataStatus == BLE_GAP_EXT_ADV_DATA_STATUS_INCOMPLETE) {
|
||||||
|
m_payload.reserve(m_advLength + disc.length_data);
|
||||||
|
m_payload.insert(m_payload.end(), disc.data, disc.data + disc.length_data);
|
||||||
|
m_dataStatus = disc.data_status;
|
||||||
|
m_advLength = m_payload.size();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_dataStatus = disc.data_status;
|
||||||
|
m_isLegacyAdv = disc.props & BLE_HCI_ADV_LEGACY_MASK;
|
||||||
# else
|
# else
|
||||||
const auto& disc = event->disc;
|
const auto& disc = event->disc;
|
||||||
# endif
|
# endif
|
||||||
@@ -87,11 +96,11 @@ const NimBLEAddress& NimBLEAdvertisedDevice::getAddress() const {
|
|||||||
/**
|
/**
|
||||||
* @brief Get the advertisement type.
|
* @brief Get the advertisement type.
|
||||||
* @return The advertising type the device is reporting:
|
* @return The advertising type the device is reporting:
|
||||||
* * BLE_HCI_ADV_TYPE_ADV_IND (0) - indirect advertising
|
* * BLE_HCI_ADV_RPT_EVTYPE_ADV_IND (0) - indirect advertising - connectable and scannable
|
||||||
* * BLE_HCI_ADV_TYPE_ADV_DIRECT_IND_HD (1) - direct advertising - high duty cycle
|
* * BLE_HCI_ADV_RPT_EVTYPE_DIR_IND (1) - direct advertising - connectable
|
||||||
* * BLE_HCI_ADV_TYPE_ADV_SCAN_IND (2) - indirect scan response
|
* * BLE_HCI_ADV_RPT_EVTYPE_SCAN_IND (2) - indirect scan response - not connectable - scannable
|
||||||
* * BLE_HCI_ADV_TYPE_ADV_NONCONN_IND (3) - indirect advertising - not connectable
|
* * BLE_HCI_ADV_RPT_EVTYPE_NONCONN_IND (3) - beacon only - not connectable - not scannable
|
||||||
* * BLE_HCI_ADV_TYPE_ADV_DIRECT_IND_LD (4) - direct advertising - low duty cycle
|
* * BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP (4) - scan response
|
||||||
*/
|
*/
|
||||||
uint8_t NimBLEAdvertisedDevice::getAdvType() const {
|
uint8_t NimBLEAdvertisedDevice::getAdvType() const {
|
||||||
return m_advType;
|
return m_advType;
|
||||||
@@ -581,7 +590,7 @@ bool NimBLEAdvertisedDevice::haveTXPower() const {
|
|||||||
return findAdvField(BLE_HS_ADV_TYPE_TX_PWR_LVL) > 0;
|
return findAdvField(BLE_HS_ADV_TYPE_TX_PWR_LVL) > 0;
|
||||||
} // haveTXPower
|
} // haveTXPower
|
||||||
|
|
||||||
# if CONFIG_BT_NIMBLE_EXT_ADV
|
# if MYNEWT_VAL(BLE_EXT_ADV)
|
||||||
/**
|
/**
|
||||||
* @brief Get the set ID of the extended advertisement.
|
* @brief Get the set ID of the extended advertisement.
|
||||||
* @return The set ID.
|
* @return The set ID.
|
||||||
@@ -618,6 +627,18 @@ uint8_t NimBLEAdvertisedDevice::getSecondaryPhy() const {
|
|||||||
uint16_t NimBLEAdvertisedDevice::getPeriodicInterval() const {
|
uint16_t NimBLEAdvertisedDevice::getPeriodicInterval() const {
|
||||||
return m_periodicItvl;
|
return m_periodicItvl;
|
||||||
} // getPeriodicInterval
|
} // getPeriodicInterval
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the advertisement data status.
|
||||||
|
* @return The advertisement data status.
|
||||||
|
* One of:
|
||||||
|
* * BLE_GAP_EXT_ADV_DATA_STATUS_COMPLETE (0) - Complete extended advertising data
|
||||||
|
* * BLE_GAP_EXT_ADV_DATA_STATUS_INCOMPLETE (1) - Incomplete extended advertising data, more to come
|
||||||
|
* * BLE_GAP_EXT_ADV_DATA_STATUS_TRUNCATED (2) - Incomplete extended advertising data, no more to come
|
||||||
|
*/
|
||||||
|
uint8_t NimBLEAdvertisedDevice::getDataStatus() const {
|
||||||
|
return m_dataStatus;
|
||||||
|
} // getDataStatus
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
uint8_t NimBLEAdvertisedDevice::findAdvField(uint8_t type, uint8_t index, size_t* data_loc) const {
|
uint8_t NimBLEAdvertisedDevice::findAdvField(uint8_t type, uint8_t index, size_t* data_loc) const {
|
||||||
@@ -731,7 +752,7 @@ std::string NimBLEAdvertisedDevice::toString() const {
|
|||||||
* @brief Get the length of the advertisement data in the payload.
|
* @brief Get the length of the advertisement data in the payload.
|
||||||
* @return The number of bytes in the payload that is from the advertisement.
|
* @return The number of bytes in the payload that is from the advertisement.
|
||||||
*/
|
*/
|
||||||
uint8_t NimBLEAdvertisedDevice::getAdvLength() const {
|
uint16_t NimBLEAdvertisedDevice::getAdvLength() const {
|
||||||
return m_advLength;
|
return m_advLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -752,12 +773,13 @@ uint8_t NimBLEAdvertisedDevice::getAddressType() const {
|
|||||||
* @return True if the device is connectable.
|
* @return True if the device is connectable.
|
||||||
*/
|
*/
|
||||||
bool NimBLEAdvertisedDevice::isConnectable() const {
|
bool NimBLEAdvertisedDevice::isConnectable() const {
|
||||||
# if CONFIG_BT_NIMBLE_EXT_ADV
|
# if MYNEWT_VAL(BLE_EXT_ADV)
|
||||||
if (m_isLegacyAdv) {
|
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_CONN_MASK) || (m_advType & BLE_HCI_ADV_DIRECT_MASK);
|
||||||
}
|
}
|
||||||
# endif
|
# endif
|
||||||
return (m_advType & BLE_HCI_ADV_CONN_MASK) || (m_advType & BLE_HCI_ADV_DIRECT_MASK);
|
|
||||||
|
return m_advType == BLE_HCI_ADV_RPT_EVTYPE_ADV_IND || m_advType == BLE_HCI_ADV_RPT_EVTYPE_DIR_IND;
|
||||||
} // isConnectable
|
} // isConnectable
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -773,13 +795,23 @@ bool NimBLEAdvertisedDevice::isScannable() const {
|
|||||||
* @return True if legacy (Bluetooth 4.x), false if extended (bluetooth 5.x).
|
* @return True if legacy (Bluetooth 4.x), false if extended (bluetooth 5.x).
|
||||||
*/
|
*/
|
||||||
bool NimBLEAdvertisedDevice::isLegacyAdvertisement() const {
|
bool NimBLEAdvertisedDevice::isLegacyAdvertisement() const {
|
||||||
# if CONFIG_BT_NIMBLE_EXT_ADV
|
# if MYNEWT_VAL(BLE_EXT_ADV)
|
||||||
return m_isLegacyAdv;
|
return m_isLegacyAdv;
|
||||||
# else
|
# else
|
||||||
return true;
|
return true;
|
||||||
# endif
|
# endif
|
||||||
} // isLegacyAdvertisement
|
} // isLegacyAdvertisement
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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.
|
* @brief Get the payload advertised by the device.
|
||||||
* @return The advertisement payload.
|
* @return The advertisement payload.
|
||||||
@@ -804,4 +836,4 @@ const std::vector<uint8_t>::const_iterator NimBLEAdvertisedDevice::end() const {
|
|||||||
return m_payload.cend();
|
return m_payload.cend();
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL */
|
#endif // CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_OBSERVER)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
|
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
|
||||||
* esp-nimble-cpp, NimBLE-Arduino contributors.
|
* esp-nimble-cpp, NimBLE-Arduino contributors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@@ -18,8 +18,8 @@
|
|||||||
#ifndef NIMBLE_CPP_ADVERTISED_DEVICE_H_
|
#ifndef NIMBLE_CPP_ADVERTISED_DEVICE_H_
|
||||||
#define NIMBLE_CPP_ADVERTISED_DEVICE_H_
|
#define NIMBLE_CPP_ADVERTISED_DEVICE_H_
|
||||||
|
|
||||||
#include "nimconfig.h"
|
#include "syscfg/syscfg.h"
|
||||||
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
|
#if CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_OBSERVER)
|
||||||
|
|
||||||
# include "NimBLEAddress.h"
|
# include "NimBLEAddress.h"
|
||||||
# include "NimBLEScan.h"
|
# include "NimBLEScan.h"
|
||||||
@@ -69,7 +69,7 @@ class NimBLEAdvertisedDevice {
|
|||||||
NimBLEAddress getTargetAddress(uint8_t index = 0) const;
|
NimBLEAddress getTargetAddress(uint8_t index = 0) const;
|
||||||
uint8_t getTargetAddressCount() const;
|
uint8_t getTargetAddressCount() const;
|
||||||
int8_t getTXPower() const;
|
int8_t getTXPower() const;
|
||||||
uint8_t getAdvLength() const;
|
uint16_t getAdvLength() const;
|
||||||
uint8_t getAddressType() const;
|
uint8_t getAddressType() const;
|
||||||
bool isAdvertisingService(const NimBLEUUID& uuid) const;
|
bool isAdvertisingService(const NimBLEUUID& uuid) const;
|
||||||
bool haveAppearance() const;
|
bool haveAppearance() const;
|
||||||
@@ -87,12 +87,14 @@ class NimBLEAdvertisedDevice {
|
|||||||
bool isConnectable() const;
|
bool isConnectable() const;
|
||||||
bool isScannable() const;
|
bool isScannable() const;
|
||||||
bool isLegacyAdvertisement() const;
|
bool isLegacyAdvertisement() const;
|
||||||
# if CONFIG_BT_NIMBLE_EXT_ADV
|
# if MYNEWT_VAL(BLE_EXT_ADV)
|
||||||
uint8_t getSetId() const;
|
uint8_t getSetId() const;
|
||||||
uint8_t getPrimaryPhy() const;
|
uint8_t getPrimaryPhy() const;
|
||||||
uint8_t getSecondaryPhy() const;
|
uint8_t getSecondaryPhy() const;
|
||||||
uint16_t getPeriodicInterval() const;
|
uint16_t getPeriodicInterval() const;
|
||||||
|
uint8_t getDataStatus() const;
|
||||||
# endif
|
# endif
|
||||||
|
operator NimBLEAddress() const;
|
||||||
|
|
||||||
const std::vector<uint8_t>& getPayload() const;
|
const std::vector<uint8_t>& getPayload() const;
|
||||||
const std::vector<uint8_t>::const_iterator begin() const;
|
const std::vector<uint8_t>::const_iterator begin() const;
|
||||||
@@ -160,10 +162,11 @@ class NimBLEAdvertisedDevice {
|
|||||||
uint8_t m_advType{};
|
uint8_t m_advType{};
|
||||||
int8_t m_rssi{};
|
int8_t m_rssi{};
|
||||||
uint8_t m_callbackSent{};
|
uint8_t m_callbackSent{};
|
||||||
uint8_t m_advLength{};
|
uint16_t m_advLength{};
|
||||||
|
|
||||||
# if CONFIG_BT_NIMBLE_EXT_ADV
|
# if MYNEWT_VAL(BLE_EXT_ADV)
|
||||||
bool m_isLegacyAdv{};
|
bool m_isLegacyAdv{};
|
||||||
|
uint8_t m_dataStatus{};
|
||||||
uint8_t m_sid{};
|
uint8_t m_sid{};
|
||||||
uint8_t m_primPhy{};
|
uint8_t m_primPhy{};
|
||||||
uint8_t m_secPhy{};
|
uint8_t m_secPhy{};
|
||||||
@@ -173,5 +176,5 @@ class NimBLEAdvertisedDevice {
|
|||||||
std::vector<uint8_t> m_payload;
|
std::vector<uint8_t> m_payload;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_OBSERVER */
|
#endif /* CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_OBSERVER) */
|
||||||
#endif /* NIMBLE_CPP_ADVERTISED_DEVICE_H_ */
|
#endif /* NIMBLE_CPP_ADVERTISED_DEVICE_H_ */
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
|
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
|
||||||
* esp-nimble-cpp, NimBLE-Arduino contributors.
|
* esp-nimble-cpp, NimBLE-Arduino contributors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@@ -15,11 +15,9 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "nimconfig.h"
|
#include "NimBLEAdvertisementData.h"
|
||||||
#if (defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER) && !CONFIG_BT_NIMBLE_EXT_ADV) || \
|
#if (CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_BROADCASTER) && !MYNEWT_VAL(BLE_EXT_ADV)) || defined(_DOXYGEN_)
|
||||||
defined(_DOXYGEN_)
|
|
||||||
|
|
||||||
# include "NimBLEAdvertisementData.h"
|
|
||||||
# include "NimBLEDevice.h"
|
# include "NimBLEDevice.h"
|
||||||
# include "NimBLEUtils.h"
|
# include "NimBLEUtils.h"
|
||||||
# include "NimBLEUUID.h"
|
# include "NimBLEUUID.h"
|
||||||
@@ -39,7 +37,7 @@ static const char* LOG_TAG = "NimBLEAdvertisementData";
|
|||||||
* @param [in] length The size of 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) {
|
bool NimBLEAdvertisementData::addData(const uint8_t* data, size_t length) {
|
||||||
if ((m_payload.size() + length) > BLE_HS_ADV_MAX_SZ) {
|
if (m_payload.size() + length > BLE_HS_ADV_MAX_SZ) {
|
||||||
NIMBLE_LOGE(LOG_TAG, "Data length exceeded");
|
NIMBLE_LOGE(LOG_TAG, "Data length exceeded");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -110,7 +108,7 @@ bool NimBLEAdvertisementData::addTxPower() {
|
|||||||
data[0] = BLE_HS_ADV_TX_PWR_LVL_LEN + 1;
|
data[0] = BLE_HS_ADV_TX_PWR_LVL_LEN + 1;
|
||||||
data[1] = BLE_HS_ADV_TYPE_TX_PWR_LVL;
|
data[1] = BLE_HS_ADV_TYPE_TX_PWR_LVL;
|
||||||
# ifndef CONFIG_IDF_TARGET_ESP32P4
|
# ifndef CONFIG_IDF_TARGET_ESP32P4
|
||||||
data[2] = NimBLEDevice::getPower();
|
data[2] = NimBLEDevice::getPower(NimBLETxPowerType::Advertise);
|
||||||
# else
|
# else
|
||||||
data[2] = 0;
|
data[2] = 0;
|
||||||
# endif
|
# endif
|
||||||
@@ -159,6 +157,7 @@ bool NimBLEAdvertisementData::addServiceUUID(const NimBLEUUID& serviceUUID) {
|
|||||||
type = BLE_HS_ADV_TYPE_COMP_UUIDS128;
|
type = BLE_HS_ADV_TYPE_COMP_UUIDS128;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
NIMBLE_LOGE(LOG_TAG, "Cannot add UUID, invalid size!");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -169,10 +168,11 @@ bool NimBLEAdvertisementData::addServiceUUID(const NimBLEUUID& serviceUUID) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (length + getPayload().size() > BLE_HS_ADV_MAX_SZ) {
|
if (length + getPayload().size() > BLE_HS_ADV_MAX_SZ) {
|
||||||
|
NIMBLE_LOGE(LOG_TAG, "Cannot add UUID, data length exceeded!");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t data[31];
|
uint8_t data[BLE_HS_ADV_MAX_SZ];
|
||||||
const uint8_t* uuid = serviceUUID.getValue();
|
const uint8_t* uuid = serviceUUID.getValue();
|
||||||
if (dataLoc == -1) {
|
if (dataLoc == -1) {
|
||||||
data[0] = 1 + bytes;
|
data[0] = 1 + bytes;
|
||||||
@@ -214,6 +214,7 @@ bool NimBLEAdvertisementData::removeServiceUUID(const NimBLEUUID& serviceUUID) {
|
|||||||
type = BLE_HS_ADV_TYPE_COMP_UUIDS128;
|
type = BLE_HS_ADV_TYPE_COMP_UUIDS128;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
NIMBLE_LOGE(LOG_TAG, "Cannot remove UUID, invalid size!");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -266,12 +267,12 @@ bool NimBLEAdvertisementData::removeServices() {
|
|||||||
* @return True if successful.
|
* @return True if successful.
|
||||||
*/
|
*/
|
||||||
bool NimBLEAdvertisementData::setManufacturerData(const uint8_t* data, size_t length) {
|
bool NimBLEAdvertisementData::setManufacturerData(const uint8_t* data, size_t length) {
|
||||||
if (length > 29) {
|
if (length > BLE_HS_ADV_MAX_FIELD_SZ) {
|
||||||
NIMBLE_LOGE(LOG_TAG, "MFG data too long");
|
NIMBLE_LOGE(LOG_TAG, "MFG data too long");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t mdata[31];
|
uint8_t mdata[BLE_HS_ADV_MAX_SZ];
|
||||||
mdata[0] = length + 1;
|
mdata[0] = length + 1;
|
||||||
mdata[1] = BLE_HS_ADV_TYPE_MFG_DATA;
|
mdata[1] = BLE_HS_ADV_TYPE_MFG_DATA;
|
||||||
memcpy(&mdata[2], data, length);
|
memcpy(&mdata[2], data, length);
|
||||||
@@ -302,12 +303,12 @@ bool NimBLEAdvertisementData::setManufacturerData(const std::vector<uint8_t>& da
|
|||||||
* @return True if successful.
|
* @return True if successful.
|
||||||
*/
|
*/
|
||||||
bool NimBLEAdvertisementData::setURI(const std::string& uri) {
|
bool NimBLEAdvertisementData::setURI(const std::string& uri) {
|
||||||
if (uri.length() > 29) {
|
if (uri.length() > BLE_HS_ADV_MAX_FIELD_SZ) {
|
||||||
NIMBLE_LOGE(LOG_TAG, "URI too long");
|
NIMBLE_LOGE(LOG_TAG, "URI too long");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t data[31];
|
uint8_t data[BLE_HS_ADV_MAX_SZ];
|
||||||
uint8_t length = 2 + uri.length();
|
uint8_t length = 2 + uri.length();
|
||||||
data[0] = length - 1;
|
data[0] = length - 1;
|
||||||
data[1] = BLE_HS_ADV_TYPE_URI;
|
data[1] = BLE_HS_ADV_TYPE_URI;
|
||||||
@@ -324,16 +325,16 @@ bool NimBLEAdvertisementData::setURI(const std::string& uri) {
|
|||||||
* @return True if successful.
|
* @return True if successful.
|
||||||
*/
|
*/
|
||||||
bool NimBLEAdvertisementData::setName(const std::string& name, bool isComplete) {
|
bool NimBLEAdvertisementData::setName(const std::string& name, bool isComplete) {
|
||||||
if (name.length() > 29) {
|
if (name.length() > BLE_HS_ADV_MAX_FIELD_SZ) {
|
||||||
NIMBLE_LOGE(LOG_TAG, "Name too long - truncating");
|
NIMBLE_LOGE(LOG_TAG, "Name too long - truncating");
|
||||||
isComplete = false;
|
isComplete = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t data[31];
|
uint8_t data[BLE_HS_ADV_MAX_SZ];
|
||||||
uint8_t length = 2 + std::min<uint8_t>(name.length(), 29);
|
uint8_t length = 2 + std::min<uint8_t>(name.length(), BLE_HS_ADV_MAX_FIELD_SZ);
|
||||||
data[0] = length - 1;
|
data[0] = length - 1;
|
||||||
data[1] = isComplete ? BLE_HS_ADV_TYPE_COMP_NAME : BLE_HS_ADV_TYPE_INCOMP_NAME;
|
data[1] = isComplete ? BLE_HS_ADV_TYPE_COMP_NAME : BLE_HS_ADV_TYPE_INCOMP_NAME;
|
||||||
memcpy(&data[2], name.c_str(), std::min<uint8_t>(name.length(), 29));
|
memcpy(&data[2], name.c_str(), std::min<uint8_t>(name.length(), BLE_HS_ADV_MAX_FIELD_SZ));
|
||||||
return addData(data, length);
|
return addData(data, length);
|
||||||
} // setName
|
} // setName
|
||||||
|
|
||||||
@@ -411,14 +412,14 @@ bool NimBLEAdvertisementData::setPartialServices32(const std::vector<NimBLEUUID>
|
|||||||
bool NimBLEAdvertisementData::setServices(bool complete, uint8_t size, const std::vector<NimBLEUUID>& uuids) {
|
bool NimBLEAdvertisementData::setServices(bool complete, uint8_t size, const std::vector<NimBLEUUID>& uuids) {
|
||||||
uint8_t bytes = size / 8;
|
uint8_t bytes = size / 8;
|
||||||
uint8_t length = 2; // start with 2 for length + type bytes
|
uint8_t length = 2; // start with 2 for length + type bytes
|
||||||
uint8_t data[31];
|
uint8_t data[BLE_HS_ADV_MAX_SZ];
|
||||||
|
|
||||||
for (const auto& uuid : uuids) {
|
for (const auto& uuid : uuids) {
|
||||||
if (uuid.bitSize() != size) {
|
if (uuid.bitSize() != size) {
|
||||||
NIMBLE_LOGE(LOG_TAG, "Service UUID(%d) invalid", size);
|
NIMBLE_LOGE(LOG_TAG, "Service UUID(%d) invalid", size);
|
||||||
continue;
|
continue;
|
||||||
} else {
|
} else {
|
||||||
if (length + bytes >= 31) {
|
if (length + bytes >= BLE_HS_ADV_MAX_SZ) {
|
||||||
NIMBLE_LOGW(LOG_TAG, "Too many services - truncating");
|
NIMBLE_LOGW(LOG_TAG, "Too many services - truncating");
|
||||||
complete = false;
|
complete = false;
|
||||||
break;
|
break;
|
||||||
@@ -441,6 +442,7 @@ bool NimBLEAdvertisementData::setServices(bool complete, uint8_t size, const std
|
|||||||
data[1] = (complete ? BLE_HS_ADV_TYPE_COMP_UUIDS128 : BLE_HS_ADV_TYPE_INCOMP_UUIDS128);
|
data[1] = (complete ? BLE_HS_ADV_TYPE_COMP_UUIDS128 : BLE_HS_ADV_TYPE_INCOMP_UUIDS128);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
NIMBLE_LOGE(LOG_TAG, "Cannot set services, invalid size!");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -458,7 +460,7 @@ bool NimBLEAdvertisementData::setServices(bool complete, uint8_t size, const std
|
|||||||
bool NimBLEAdvertisementData::setServiceData(const NimBLEUUID& uuid, const uint8_t* data, size_t length) {
|
bool NimBLEAdvertisementData::setServiceData(const NimBLEUUID& uuid, const uint8_t* data, size_t length) {
|
||||||
uint8_t uuidBytes = uuid.bitSize() / 8;
|
uint8_t uuidBytes = uuid.bitSize() / 8;
|
||||||
uint8_t sDataLen = 2 + uuidBytes + length;
|
uint8_t sDataLen = 2 + uuidBytes + length;
|
||||||
if (sDataLen > 31) {
|
if (sDataLen > BLE_HS_ADV_MAX_SZ) {
|
||||||
NIMBLE_LOGE(LOG_TAG, "Service Data too long");
|
NIMBLE_LOGE(LOG_TAG, "Service Data too long");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -475,6 +477,7 @@ bool NimBLEAdvertisementData::setServiceData(const NimBLEUUID& uuid, const uint8
|
|||||||
type = BLE_HS_ADV_TYPE_SVC_DATA_UUID128;
|
type = BLE_HS_ADV_TYPE_SVC_DATA_UUID128;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
NIMBLE_LOGE(LOG_TAG, "Cannot set service data, invalid size!");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -483,7 +486,7 @@ bool NimBLEAdvertisementData::setServiceData(const NimBLEUUID& uuid, const uint8
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t sData[31];
|
uint8_t sData[BLE_HS_ADV_MAX_SZ];
|
||||||
sData[0] = uuidBytes + length + 1;
|
sData[0] = uuidBytes + length + 1;
|
||||||
sData[1] = type;
|
sData[1] = type;
|
||||||
memcpy(&sData[2], uuid.getValue(), uuidBytes);
|
memcpy(&sData[2], uuid.getValue(), uuidBytes);
|
||||||
@@ -580,4 +583,4 @@ std::string NimBLEAdvertisementData::toString() const {
|
|||||||
return str;
|
return str;
|
||||||
} // toString
|
} // toString
|
||||||
|
|
||||||
#endif // CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_BROADCASTER && !CONFIG_BT_NIMBLE_EXT_ADV
|
#endif // (CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_BROADCASTER) && !MYNEWT_VAL(BLE_EXT_ADV)) || defined(_DOXYGEN_)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
|
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
|
||||||
* esp-nimble-cpp, NimBLE-Arduino contributors.
|
* esp-nimble-cpp, NimBLE-Arduino contributors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@@ -18,9 +18,8 @@
|
|||||||
#ifndef NIMBLE_CPP_ADVERTISEMENT_DATA_H_
|
#ifndef NIMBLE_CPP_ADVERTISEMENT_DATA_H_
|
||||||
#define NIMBLE_CPP_ADVERTISEMENT_DATA_H_
|
#define NIMBLE_CPP_ADVERTISEMENT_DATA_H_
|
||||||
|
|
||||||
#include "nimconfig.h"
|
#include "syscfg/syscfg.h"
|
||||||
#if (defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER) && !CONFIG_BT_NIMBLE_EXT_ADV) || \
|
#if (CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_BROADCASTER) && !MYNEWT_VAL(BLE_EXT_ADV)) || defined(_DOXYGEN_)
|
||||||
defined(_DOXYGEN_)
|
|
||||||
|
|
||||||
# include <cstdint>
|
# include <cstdint>
|
||||||
# include <string>
|
# include <string>
|
||||||
@@ -38,7 +37,7 @@ class NimBLEAdvertisementData {
|
|||||||
bool addData(const uint8_t* data, size_t length);
|
bool addData(const uint8_t* data, size_t length);
|
||||||
bool addData(const std::vector<uint8_t>& data);
|
bool addData(const std::vector<uint8_t>& data);
|
||||||
bool setAppearance(uint16_t appearance);
|
bool setAppearance(uint16_t appearance);
|
||||||
bool setFlags(uint8_t);
|
bool setFlags(uint8_t flag);
|
||||||
bool addTxPower();
|
bool addTxPower();
|
||||||
bool setPreferredParams(uint16_t minInterval, uint16_t maxInterval);
|
bool setPreferredParams(uint16_t minInterval, uint16_t maxInterval);
|
||||||
bool addServiceUUID(const NimBLEUUID& serviceUUID);
|
bool addServiceUUID(const NimBLEUUID& serviceUUID);
|
||||||
@@ -75,5 +74,5 @@ class NimBLEAdvertisementData {
|
|||||||
std::vector<uint8_t> m_payload{};
|
std::vector<uint8_t> m_payload{};
|
||||||
}; // NimBLEAdvertisementData
|
}; // NimBLEAdvertisementData
|
||||||
|
|
||||||
#endif // CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_BROADCASTER && !CONFIG_BT_NIMBLE_EXT_ADV
|
#endif // (CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_BROADCASTER) && !MYNEWT_VAL(BLE_EXT_ADV)) || defined(_DOXYGEN_)
|
||||||
#endif // NIMBLE_CPP_ADVERTISEMENT_DATA_H_
|
#endif // NIMBLE_CPP_ADVERTISEMENT_DATA_H_
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
|
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
|
||||||
* esp-nimble-cpp, NimBLE-Arduino contributors.
|
* esp-nimble-cpp, NimBLE-Arduino contributors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@@ -15,16 +15,14 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "nimconfig.h"
|
#include "NimBLEAdvertising.h"
|
||||||
#if (defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER) && !CONFIG_BT_NIMBLE_EXT_ADV) || \
|
#if (CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_BROADCASTER) && !MYNEWT_VAL(BLE_EXT_ADV)) || defined(_DOXYGEN_)
|
||||||
defined(_DOXYGEN_)
|
|
||||||
|
|
||||||
# if defined(CONFIG_NIMBLE_CPP_IDF)
|
# if defined(CONFIG_NIMBLE_CPP_IDF)
|
||||||
# include "services/gap/ble_svc_gap.h"
|
# include "services/gap/ble_svc_gap.h"
|
||||||
# else
|
# else
|
||||||
# include "nimble/nimble/host/services/gap/include/services/gap/ble_svc_gap.h"
|
# include "nimble/nimble/host/services/gap/include/services/gap/ble_svc_gap.h"
|
||||||
# endif
|
# endif
|
||||||
# include "NimBLEAdvertising.h"
|
|
||||||
# include "NimBLEDevice.h"
|
# include "NimBLEDevice.h"
|
||||||
# include "NimBLEServer.h"
|
# include "NimBLEServer.h"
|
||||||
# include "NimBLEUtils.h"
|
# include "NimBLEUtils.h"
|
||||||
@@ -44,7 +42,7 @@ NimBLEAdvertising::NimBLEAdvertising()
|
|||||||
m_duration{BLE_HS_FOREVER},
|
m_duration{BLE_HS_FOREVER},
|
||||||
m_scanResp{false},
|
m_scanResp{false},
|
||||||
m_advDataSet{false} {
|
m_advDataSet{false} {
|
||||||
# if !defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
|
# if !MYNEWT_VAL(BLE_ROLE_PERIPHERAL)
|
||||||
m_advParams.conn_mode = BLE_GAP_CONN_MODE_NON;
|
m_advParams.conn_mode = BLE_GAP_CONN_MODE_NON;
|
||||||
# else
|
# else
|
||||||
m_advParams.conn_mode = BLE_GAP_CONN_MODE_UND;
|
m_advParams.conn_mode = BLE_GAP_CONN_MODE_UND;
|
||||||
@@ -197,10 +195,11 @@ bool NimBLEAdvertising::start(uint32_t duration, const NimBLEAddress* dirAddr) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
# if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
|
# if MYNEWT_VAL(BLE_ROLE_PERIPHERAL)
|
||||||
NimBLEServer* pServer = NimBLEDevice::getServer();
|
NimBLEServer* pServer = NimBLEDevice::getServer();
|
||||||
if (pServer != nullptr) {
|
if (pServer != nullptr && !pServer->start()) { // make sure the GATT server is ready before advertising
|
||||||
pServer->start(); // make sure the GATT server is ready before advertising
|
NIMBLE_LOGE(LOG_TAG, "Failed to start GATT server");
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
@@ -222,7 +221,7 @@ bool NimBLEAdvertising::start(uint32_t duration, const NimBLEAddress* dirAddr) {
|
|||||||
duration = BLE_HS_FOREVER;
|
duration = BLE_HS_FOREVER;
|
||||||
}
|
}
|
||||||
|
|
||||||
# if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
|
# if MYNEWT_VAL(BLE_ROLE_PERIPHERAL)
|
||||||
int rc = ble_gap_adv_start(NimBLEDevice::m_ownAddrType,
|
int rc = ble_gap_adv_start(NimBLEDevice::m_ownAddrType,
|
||||||
(dirAddr != nullptr) ? dirAddr->getBase() : NULL,
|
(dirAddr != nullptr) ? dirAddr->getBase() : NULL,
|
||||||
duration,
|
duration,
|
||||||
@@ -408,7 +407,7 @@ bool NimBLEAdvertising::refreshAdvertisingData() {
|
|||||||
* @return True if the service was added successfully.
|
* @return True if the service was added successfully.
|
||||||
*/
|
*/
|
||||||
bool NimBLEAdvertising::addServiceUUID(const NimBLEUUID& serviceUUID) {
|
bool NimBLEAdvertising::addServiceUUID(const NimBLEUUID& serviceUUID) {
|
||||||
if (!m_advData.addServiceUUID(serviceUUID) && m_scanResp) {
|
if (!m_advData.addServiceUUID(serviceUUID)) {
|
||||||
if (!m_scanData.addServiceUUID(serviceUUID)) {
|
if (!m_scanData.addServiceUUID(serviceUUID)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -466,7 +465,7 @@ bool NimBLEAdvertising::removeServices() {
|
|||||||
* @return True if the appearance was set successfully.
|
* @return True if the appearance was set successfully.
|
||||||
*/
|
*/
|
||||||
bool NimBLEAdvertising::setAppearance(uint16_t appearance) {
|
bool NimBLEAdvertising::setAppearance(uint16_t appearance) {
|
||||||
if (!m_advData.setAppearance(appearance) && m_scanResp) {
|
if (!m_advData.setAppearance(appearance)) {
|
||||||
if (!m_scanData.setAppearance(appearance)) {
|
if (!m_scanData.setAppearance(appearance)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -484,7 +483,7 @@ bool NimBLEAdvertising::setAppearance(uint16_t appearance) {
|
|||||||
* @details Range = 0x0006(7.5ms) to 0x0C80(4000ms), values not within the range will be limited to this range.
|
* @details Range = 0x0006(7.5ms) to 0x0C80(4000ms), values not within the range will be limited to this range.
|
||||||
*/
|
*/
|
||||||
bool NimBLEAdvertising::setPreferredParams(uint16_t minInterval, uint16_t maxInterval) {
|
bool NimBLEAdvertising::setPreferredParams(uint16_t minInterval, uint16_t maxInterval) {
|
||||||
if (!m_advData.setPreferredParams(minInterval, maxInterval) && m_scanResp) {
|
if (!m_advData.setPreferredParams(minInterval, maxInterval)) {
|
||||||
if (!m_scanData.setPreferredParams(minInterval, maxInterval)) {
|
if (!m_scanData.setPreferredParams(minInterval, maxInterval)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -499,7 +498,7 @@ bool NimBLEAdvertising::setPreferredParams(uint16_t minInterval, uint16_t maxInt
|
|||||||
* @return True if the transmission power level was added successfully.
|
* @return True if the transmission power level was added successfully.
|
||||||
*/
|
*/
|
||||||
bool NimBLEAdvertising::addTxPower() {
|
bool NimBLEAdvertising::addTxPower() {
|
||||||
if (!m_advData.addTxPower() && m_scanResp) {
|
if (!m_advData.addTxPower()) {
|
||||||
if (!m_scanData.addTxPower()) {
|
if (!m_scanData.addTxPower()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -537,7 +536,7 @@ bool NimBLEAdvertising::setName(const std::string& name) {
|
|||||||
* @return True if the manufacturer data was set successfully.
|
* @return True if the manufacturer data was set successfully.
|
||||||
*/
|
*/
|
||||||
bool NimBLEAdvertising::setManufacturerData(const uint8_t* data, size_t length) {
|
bool NimBLEAdvertising::setManufacturerData(const uint8_t* data, size_t length) {
|
||||||
if (!m_advData.setManufacturerData(data, length) && m_scanResp) {
|
if (!m_advData.setManufacturerData(data, length)) {
|
||||||
if (!m_scanData.setManufacturerData(data, length)) {
|
if (!m_scanData.setManufacturerData(data, length)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -571,7 +570,7 @@ bool NimBLEAdvertising::setManufacturerData(const std::vector<uint8_t>& data) {
|
|||||||
* @return True if the URI was set successfully.
|
* @return True if the URI was set successfully.
|
||||||
*/
|
*/
|
||||||
bool NimBLEAdvertising::setURI(const std::string& uri) {
|
bool NimBLEAdvertising::setURI(const std::string& uri) {
|
||||||
if (!m_advData.setURI(uri) && m_scanResp) {
|
if (!m_advData.setURI(uri)) {
|
||||||
if (!m_scanData.setURI(uri)) {
|
if (!m_scanData.setURI(uri)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -590,7 +589,7 @@ bool NimBLEAdvertising::setURI(const std::string& uri) {
|
|||||||
* @note If data length is 0 the service data will not be advertised.
|
* @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) {
|
bool NimBLEAdvertising::setServiceData(const NimBLEUUID& uuid, const uint8_t* data, size_t length) {
|
||||||
if (!m_advData.setServiceData(uuid, data, length) && m_scanResp) {
|
if (!m_advData.setServiceData(uuid, data, length)) {
|
||||||
if (!m_scanData.setServiceData(uuid, data, length)) {
|
if (!m_scanData.setServiceData(uuid, data, length)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -622,4 +621,4 @@ bool NimBLEAdvertising::setServiceData(const NimBLEUUID& uuid, const std::string
|
|||||||
return setServiceData(uuid, reinterpret_cast<const uint8_t*>(data.data()), data.length());
|
return setServiceData(uuid, reinterpret_cast<const uint8_t*>(data.data()), data.length());
|
||||||
} // setServiceData
|
} // setServiceData
|
||||||
|
|
||||||
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_BROADCASTER && !CONFIG_BT_NIMBLE_EXT_ADV */
|
#endif // (CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_BROADCASTER) && !MYNEWT_VAL(BLE_EXT_ADV)) || defined(_DOXYGEN_)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
|
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
|
||||||
* esp-nimble-cpp, NimBLE-Arduino contributors.
|
* esp-nimble-cpp, NimBLE-Arduino contributors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@@ -18,9 +18,8 @@
|
|||||||
#ifndef NIMBLE_CPP_ADVERTISING_H_
|
#ifndef NIMBLE_CPP_ADVERTISING_H_
|
||||||
#define NIMBLE_CPP_ADVERTISING_H_
|
#define NIMBLE_CPP_ADVERTISING_H_
|
||||||
|
|
||||||
#include "nimconfig.h"
|
#include "syscfg/syscfg.h"
|
||||||
#if (defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER) && !CONFIG_BT_NIMBLE_EXT_ADV) || \
|
#if (CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_BROADCASTER) && !MYNEWT_VAL(BLE_EXT_ADV)) || defined(_DOXYGEN_)
|
||||||
defined(_DOXYGEN_)
|
|
||||||
|
|
||||||
# if defined(CONFIG_NIMBLE_CPP_IDF)
|
# if defined(CONFIG_NIMBLE_CPP_IDF)
|
||||||
# include "host/ble_gap.h"
|
# include "host/ble_gap.h"
|
||||||
@@ -106,5 +105,5 @@ class NimBLEAdvertising {
|
|||||||
bool m_advDataSet : 1;
|
bool m_advDataSet : 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_BROADCASTER && !CONFIG_BT_NIMBLE_EXT_ADV */
|
#endif // (CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_BROADCASTER) && !MYNEWT_VAL(BLE_EXT_ADV)) || defined(_DOXYGEN_)
|
||||||
#endif /* NIMBLE_CPP_ADVERTISING_H_ */
|
#endif // NIMBLE_CPP_ADVERTISING_H_
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
|
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
|
||||||
* esp-nimble-cpp, NimBLE-Arduino contributors.
|
* esp-nimble-cpp, NimBLE-Arduino contributors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@@ -15,8 +15,8 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "nimconfig.h"
|
#include "NimBLEAttValue.h"
|
||||||
#if defined(CONFIG_BT_ENABLED)
|
#if CONFIG_BT_NIMBLE_ENABLED
|
||||||
|
|
||||||
# if defined(CONFIG_NIMBLE_CPP_IDF)
|
# if defined(CONFIG_NIMBLE_CPP_IDF)
|
||||||
# include "nimble/nimble_npl.h"
|
# include "nimble/nimble_npl.h"
|
||||||
@@ -24,7 +24,10 @@
|
|||||||
# include "nimble/nimble/include/nimble/nimble_npl.h"
|
# include "nimble/nimble/include/nimble/nimble_npl.h"
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
# include "NimBLEAttValue.h"
|
# include "NimBLEUtils.h"
|
||||||
|
# include "NimBLELog.h"
|
||||||
|
|
||||||
|
static const char* LOG_TAG = "NimBLEAttValue";
|
||||||
|
|
||||||
// Default constructor implementation.
|
// Default constructor implementation.
|
||||||
NimBLEAttValue::NimBLEAttValue(uint16_t init_len, uint16_t max_len)
|
NimBLEAttValue::NimBLEAttValue(uint16_t init_len, uint16_t max_len)
|
||||||
@@ -32,19 +35,23 @@ NimBLEAttValue::NimBLEAttValue(uint16_t init_len, uint16_t max_len)
|
|||||||
m_attr_max_len{std::min<uint16_t>(BLE_ATT_ATTR_MAX_LEN, max_len)},
|
m_attr_max_len{std::min<uint16_t>(BLE_ATT_ATTR_MAX_LEN, max_len)},
|
||||||
m_attr_len{},
|
m_attr_len{},
|
||||||
m_capacity{init_len}
|
m_capacity{init_len}
|
||||||
# if CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED
|
# if MYNEWT_VAL(NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED)
|
||||||
,
|
,
|
||||||
m_timestamp{}
|
m_timestamp{}
|
||||||
# endif
|
# endif
|
||||||
{
|
{
|
||||||
NIMBLE_CPP_DEBUG_ASSERT(m_attr_value);
|
NIMBLE_CPP_DEBUG_ASSERT(m_attr_value);
|
||||||
|
if (m_attr_value == nullptr) {
|
||||||
|
NIMBLE_LOGE(LOG_TAG, "Failed to calloc ctx");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Value constructor implementation.
|
// Value constructor implementation.
|
||||||
NimBLEAttValue::NimBLEAttValue(const uint8_t* value, uint16_t len, uint16_t max_len) : NimBLEAttValue(len, max_len) {
|
NimBLEAttValue::NimBLEAttValue(const uint8_t* value, uint16_t len, uint16_t max_len) : NimBLEAttValue(len, max_len) {
|
||||||
memcpy(m_attr_value, value, len);
|
if (m_attr_value != nullptr) {
|
||||||
m_attr_value[len] = '\0';
|
memcpy(m_attr_value, value, len);
|
||||||
m_attr_len = len;
|
m_attr_len = len;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Destructor implementation.
|
// Destructor implementation.
|
||||||
@@ -81,6 +88,10 @@ NimBLEAttValue& NimBLEAttValue::operator=(const NimBLEAttValue& source) {
|
|||||||
void NimBLEAttValue::deepCopy(const NimBLEAttValue& source) {
|
void NimBLEAttValue::deepCopy(const NimBLEAttValue& source) {
|
||||||
uint8_t* res = static_cast<uint8_t*>(realloc(m_attr_value, source.m_capacity + 1));
|
uint8_t* res = static_cast<uint8_t*>(realloc(m_attr_value, source.m_capacity + 1));
|
||||||
NIMBLE_CPP_DEBUG_ASSERT(res);
|
NIMBLE_CPP_DEBUG_ASSERT(res);
|
||||||
|
if (res == nullptr) {
|
||||||
|
NIMBLE_LOGE(LOG_TAG, "Failed to realloc deepCopy");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
ble_npl_hw_enter_critical();
|
ble_npl_hw_enter_critical();
|
||||||
m_attr_value = res;
|
m_attr_value = res;
|
||||||
@@ -94,7 +105,8 @@ void NimBLEAttValue::deepCopy(const NimBLEAttValue& source) {
|
|||||||
|
|
||||||
// Set the value of the attribute.
|
// Set the value of the attribute.
|
||||||
bool NimBLEAttValue::setValue(const uint8_t* value, uint16_t len) {
|
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_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);
|
append(value, len);
|
||||||
return memcmp(m_attr_value, value, len) == 0 && m_attr_len == len;
|
return memcmp(m_attr_value, value, len) == 0 && m_attr_len == len;
|
||||||
}
|
}
|
||||||
@@ -106,7 +118,7 @@ NimBLEAttValue& NimBLEAttValue::append(const uint8_t* value, uint16_t len) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ((m_attr_len + len) > m_attr_max_len) {
|
if ((m_attr_len + len) > m_attr_max_len) {
|
||||||
NIMBLE_LOGE("NimBLEAttValue", "val > max, len=%u, max=%u", len, m_attr_max_len);
|
NIMBLE_LOGE(LOG_TAG, "val > max, len=%u, max=%u", len, m_attr_max_len);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,8 +129,12 @@ NimBLEAttValue& NimBLEAttValue::append(const uint8_t* value, uint16_t len) {
|
|||||||
m_capacity = new_len;
|
m_capacity = new_len;
|
||||||
}
|
}
|
||||||
NIMBLE_CPP_DEBUG_ASSERT(res);
|
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
|
# if MYNEWT_VAL(NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED)
|
||||||
time_t t = time(nullptr);
|
time_t t = time(nullptr);
|
||||||
# else
|
# else
|
||||||
time_t t = 0;
|
time_t t = 0;
|
||||||
@@ -135,4 +151,13 @@ NimBLEAttValue& NimBLEAttValue::append(const uint8_t* value, uint16_t len) {
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // CONFIG_BT_ENABLED
|
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_NIMBLE_ENABLED
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
|
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
|
||||||
* esp-nimble-cpp, NimBLE-Arduino contributors.
|
* esp-nimble-cpp, NimBLE-Arduino contributors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@@ -17,34 +17,44 @@
|
|||||||
|
|
||||||
#ifndef NIMBLE_CPP_ATTVALUE_H
|
#ifndef NIMBLE_CPP_ATTVALUE_H
|
||||||
#define NIMBLE_CPP_ATTVALUE_H
|
#define NIMBLE_CPP_ATTVALUE_H
|
||||||
#include "nimconfig.h"
|
|
||||||
#if defined(CONFIG_BT_ENABLED)
|
#include "syscfg/syscfg.h"
|
||||||
|
#if CONFIG_BT_NIMBLE_ENABLED
|
||||||
|
|
||||||
# ifdef NIMBLE_CPP_ARDUINO_STRING_AVAILABLE
|
# ifdef NIMBLE_CPP_ARDUINO_STRING_AVAILABLE
|
||||||
# include <Arduino.h>
|
# include <Arduino.h>
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
# include "NimBLELog.h"
|
|
||||||
# include <string>
|
# include <string>
|
||||||
# include <vector>
|
# include <vector>
|
||||||
# include <ctime>
|
# include <ctime>
|
||||||
# include <cstring>
|
# include <cstring>
|
||||||
# include <cstdint>
|
# include <cstdint>
|
||||||
|
|
||||||
# ifndef CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED
|
# ifndef MYNEWT_VAL_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED
|
||||||
# define CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED 0
|
# ifndef CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED
|
||||||
|
# define MYNEWT_VAL_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED 0
|
||||||
|
# else
|
||||||
|
# define MYNEWT_VAL_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED
|
||||||
|
# endif
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
# ifndef BLE_ATT_ATTR_MAX_LEN
|
# ifndef BLE_ATT_ATTR_MAX_LEN
|
||||||
# define BLE_ATT_ATTR_MAX_LEN 512
|
# define BLE_ATT_ATTR_MAX_LEN 512
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
# if !defined(CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH)
|
# ifndef MYNEWT_VAL_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH
|
||||||
# define CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH 20
|
# ifndef CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH
|
||||||
# elif CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH > BLE_ATT_ATTR_MAX_LEN
|
# define MYNEWT_VAL_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH 20
|
||||||
# error CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH cannot be larger than 512 (BLE_ATT_ATTR_MAX_LEN)
|
# else
|
||||||
# elif CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH < 1
|
# define MYNEWT_VAL_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH
|
||||||
# error CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH cannot be less than 1; Range = 1 : 512
|
# endif
|
||||||
|
# endif
|
||||||
|
|
||||||
|
# if MYNEWT_VAL(NIMBLE_CPP_ATT_VALUE_INIT_LENGTH) > BLE_ATT_ATTR_MAX_LEN
|
||||||
|
# error NIMBLE_CPP_ATT_VALUE_INIT_LENGTH cannot be larger than 512 (BLE_ATT_ATTR_MAX_LEN)
|
||||||
|
# elif MYNEWT_VAL(NIMBLE_CPP_ATT_VALUE_INIT_LENGTH) < 1
|
||||||
|
# error NIMBLE_CPP_ATT_VALUE_INIT_LENGTH cannot be less than 1; Range = 1 : 512
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
/* Used to determine if the type passed to a template has a data() and size() method. */
|
/* Used to determine if the type passed to a template has a data() and size() method. */
|
||||||
@@ -63,6 +73,14 @@ template <typename T>
|
|||||||
struct Has_c_str_length<T, decltype(void(std::declval<T&>().c_str())), decltype(void(std::declval<T&>().length()))>
|
struct Has_c_str_length<T, decltype(void(std::declval<T&>().c_str())), decltype(void(std::declval<T&>().length()))>
|
||||||
: std::true_type {};
|
: std::true_type {};
|
||||||
|
|
||||||
|
/* Used to determine if the type passed to a template has a value_type member (std::vector, std::array, std::string, etc.). */
|
||||||
|
template <typename T, typename = void>
|
||||||
|
struct Has_value_type : std::false_type {};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct Has_value_type<T, decltype(void(sizeof(typename T::value_type)))>
|
||||||
|
: std::true_type {};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief A specialized container class to hold BLE attribute values.
|
* @brief A specialized container class to hold BLE attribute values.
|
||||||
* @details This class is designed to be more memory efficient than using\n
|
* @details This class is designed to be more memory efficient than using\n
|
||||||
@@ -74,7 +92,7 @@ class NimBLEAttValue {
|
|||||||
uint16_t m_attr_max_len{};
|
uint16_t m_attr_max_len{};
|
||||||
uint16_t m_attr_len{};
|
uint16_t m_attr_len{};
|
||||||
uint16_t m_capacity{};
|
uint16_t m_capacity{};
|
||||||
# if CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED
|
# if MYNEWT_VAL(NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED)
|
||||||
time_t m_timestamp{};
|
time_t m_timestamp{};
|
||||||
# endif
|
# endif
|
||||||
void deepCopy(const NimBLEAttValue& source);
|
void deepCopy(const NimBLEAttValue& source);
|
||||||
@@ -85,7 +103,7 @@ class NimBLEAttValue {
|
|||||||
* @param[in] init_len The initial size in bytes.
|
* @param[in] init_len The initial size in bytes.
|
||||||
* @param[in] max_len The max size in bytes that the value can be.
|
* @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 = MYNEWT_VAL(NIMBLE_CPP_ATT_VALUE_INIT_LENGTH), uint16_t max_len = BLE_ATT_ATTR_MAX_LEN);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Construct with an initial value from a buffer.
|
* @brief Construct with an initial value from a buffer.
|
||||||
@@ -170,7 +188,7 @@ class NimBLEAttValue {
|
|||||||
/** @brief Iterator end */
|
/** @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 MYNEWT_VAL(NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED)
|
||||||
/** @brief Returns a timestamp of when the value was last updated */
|
/** @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; }
|
||||||
|
|
||||||
@@ -210,7 +228,7 @@ class NimBLEAttValue {
|
|||||||
|
|
||||||
const NimBLEAttValue& getValue(time_t* timestamp = nullptr) const {
|
const NimBLEAttValue& getValue(time_t* timestamp = nullptr) const {
|
||||||
if (timestamp != nullptr) {
|
if (timestamp != nullptr) {
|
||||||
# if CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED
|
# if MYNEWT_VAL(NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED)
|
||||||
*timestamp = m_timestamp;
|
*timestamp = m_timestamp;
|
||||||
# else
|
# else
|
||||||
*timestamp = 0;
|
*timestamp = 0;
|
||||||
@@ -264,13 +282,32 @@ class NimBLEAttValue {
|
|||||||
/**
|
/**
|
||||||
* @brief Template to set value to the value of <type\>val.
|
* @brief Template to set value to the value of <type\>val.
|
||||||
* @param [in] v The <type\>value to set.
|
* @param [in] v The <type\>value to set.
|
||||||
* @details Only used if the <type\> has a `data()` and `size()` method.
|
* @details Only used if the <type\> has a `data()` and `size()` method with `value_type`.
|
||||||
|
* Correctly calculates byte size for containers with multi-byte element types.
|
||||||
*/
|
*/
|
||||||
template <typename T>
|
template <typename T>
|
||||||
# ifdef _DOXYGEN_
|
# ifdef _DOXYGEN_
|
||||||
bool
|
bool
|
||||||
# else
|
# else
|
||||||
typename std::enable_if<Has_data_size<T>::value, bool>::type
|
typename std::enable_if<Has_data_size<T>::value && Has_value_type<T>::value, bool>::type
|
||||||
|
# endif
|
||||||
|
setValue(const T& v) {
|
||||||
|
return setValue(
|
||||||
|
reinterpret_cast<const uint8_t*>(v.data()),
|
||||||
|
v.size() * sizeof(typename T::value_type)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Template to set value to the value of <type\>val.
|
||||||
|
* @param [in] v The <type\>value to set.
|
||||||
|
* @details Only used if the <type\> has a `data()` and `size()` method without `value_type`.
|
||||||
|
*/
|
||||||
|
template <typename T>
|
||||||
|
# ifdef _DOXYGEN_
|
||||||
|
bool
|
||||||
|
# else
|
||||||
|
typename std::enable_if<Has_data_size<T>::value && !Has_value_type<T>::value, bool>::type
|
||||||
# endif
|
# endif
|
||||||
setValue(const T& v) {
|
setValue(const T& v) {
|
||||||
return setValue(reinterpret_cast<const uint8_t*>(v.data()), v.size());
|
return setValue(reinterpret_cast<const uint8_t*>(v.data()), v.size());
|
||||||
@@ -285,7 +322,11 @@ class NimBLEAttValue {
|
|||||||
template <typename T>
|
template <typename T>
|
||||||
typename std::enable_if<!std::is_pointer<T>::value, bool>::type setValue(const T& s) {
|
typename std::enable_if<!std::is_pointer<T>::value, bool>::type setValue(const T& s) {
|
||||||
if constexpr (Has_data_size<T>::value) {
|
if constexpr (Has_data_size<T>::value) {
|
||||||
return setValue(reinterpret_cast<const uint8_t*>(s.data()), s.size());
|
if constexpr (Has_value_type<T>::value) {
|
||||||
|
return setValue(reinterpret_cast<const uint8_t*>(s.data()), s.size() * sizeof(typename T::value_type));
|
||||||
|
} else {
|
||||||
|
return setValue(reinterpret_cast<const uint8_t*>(s.data()), s.size());
|
||||||
|
}
|
||||||
} else if constexpr (Has_c_str_length<T>::value) {
|
} else if constexpr (Has_c_str_length<T>::value) {
|
||||||
return setValue(reinterpret_cast<const uint8_t*>(s.c_str()), s.length());
|
return setValue(reinterpret_cast<const uint8_t*>(s.c_str()), s.length());
|
||||||
} else {
|
} else {
|
||||||
@@ -306,27 +347,24 @@ class NimBLEAttValue {
|
|||||||
*/
|
*/
|
||||||
template <typename T>
|
template <typename T>
|
||||||
T getValue(time_t* timestamp = nullptr, bool skipSizeCheck = false) const {
|
T getValue(time_t* timestamp = nullptr, bool skipSizeCheck = false) const {
|
||||||
if (!skipSizeCheck && size() < sizeof(T)) {
|
|
||||||
return T();
|
|
||||||
}
|
|
||||||
if (timestamp != nullptr) {
|
if (timestamp != nullptr) {
|
||||||
# if CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED
|
# if MYNEWT_VAL(NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED)
|
||||||
*timestamp = m_timestamp;
|
*timestamp = m_timestamp;
|
||||||
# else
|
# else
|
||||||
*timestamp = 0;
|
*timestamp = 0;
|
||||||
# endif
|
# endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!skipSizeCheck && size() < sizeof(T)) {
|
||||||
|
return T();
|
||||||
|
}
|
||||||
return *(reinterpret_cast<const T*>(m_attr_value));
|
return *(reinterpret_cast<const T*>(m_attr_value));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*********************** Operators ************************/
|
/*********************** Operators ************************/
|
||||||
|
|
||||||
/** @brief Subscript operator */
|
/** @brief Subscript operator */
|
||||||
uint8_t operator[](int pos) const {
|
uint8_t operator[](int pos) const;
|
||||||
NIMBLE_CPP_DEBUG_ASSERT(pos < m_attr_len);
|
|
||||||
return m_attr_value[pos];
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @brief Operator; Get the value as a std::vector<uint8_t>. */
|
/** @brief Operator; Get the value as a std::vector<uint8_t>. */
|
||||||
operator std::vector<uint8_t>() const { return std::vector<uint8_t>(m_attr_value, m_attr_value + m_attr_len); }
|
operator std::vector<uint8_t>() const { return std::vector<uint8_t>(m_attr_value, m_attr_value + m_attr_len); }
|
||||||
@@ -366,5 +404,5 @@ class NimBLEAttValue {
|
|||||||
# endif
|
# endif
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /*(CONFIG_BT_ENABLED) */
|
#endif // CONFIG_BT_NIMBLE_ENABLED
|
||||||
#endif /* NIMBLE_CPP_ATTVALUE_H_ */
|
#endif // NIMBLE_CPP_ATTVALUE_H_
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
|
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
|
||||||
* esp-nimble-cpp, NimBLE-Arduino contributors.
|
* esp-nimble-cpp, NimBLE-Arduino contributors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@@ -18,8 +18,8 @@
|
|||||||
#ifndef NIMBLE_CPP_ATTRIBUTE_H_
|
#ifndef NIMBLE_CPP_ATTRIBUTE_H_
|
||||||
#define NIMBLE_CPP_ATTRIBUTE_H_
|
#define NIMBLE_CPP_ATTRIBUTE_H_
|
||||||
|
|
||||||
#include "nimconfig.h"
|
#include "syscfg/syscfg.h"
|
||||||
#if defined(CONFIG_BT_ENABLED) && (defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) || defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL))
|
#if CONFIG_BT_NIMBLE_ENABLED && (MYNEWT_VAL(BLE_ROLE_PERIPHERAL) || MYNEWT_VAL(BLE_ROLE_CENTRAL))
|
||||||
|
|
||||||
# include "NimBLEUUID.h"
|
# include "NimBLEUUID.h"
|
||||||
|
|
||||||
@@ -56,5 +56,5 @@ class NimBLEAttribute {
|
|||||||
uint16_t m_handle{0};
|
uint16_t m_handle{0};
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // CONFIG_BT_ENABLED && (CONFIG_BT_NIMBLE_ROLE_PERIPHERAL || CONFIG_BT_NIMBLE_ROLE_CENTRAL)
|
#endif // CONFIG_BT_NIMBLE_ENABLED && (MYNEWT_VAL(BLE_ROLE_PERIPHERAL) || MYNEWT_VAL(BLE_ROLE_CENTRAL))
|
||||||
#endif // NIMBLE_CPP_ATTRIBUTE_H_
|
#endif // NIMBLE_CPP_ATTRIBUTE_H_
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
|
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
|
||||||
* esp-nimble-cpp, NimBLE-Arduino contributors.
|
* esp-nimble-cpp, NimBLE-Arduino contributors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@@ -15,10 +15,9 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "nimconfig.h"
|
#include "NimBLEBeacon.h"
|
||||||
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
|
#if CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_BROADCASTER)
|
||||||
|
|
||||||
# include "NimBLEBeacon.h"
|
|
||||||
# include "NimBLEUUID.h"
|
# include "NimBLEUUID.h"
|
||||||
# include "NimBLELog.h"
|
# include "NimBLELog.h"
|
||||||
|
|
||||||
@@ -138,4 +137,4 @@ void NimBLEBeacon::setSignalPower(int8_t signalPower) {
|
|||||||
m_beaconData.signalPower = signalPower;
|
m_beaconData.signalPower = signalPower;
|
||||||
} // setSignalPower
|
} // setSignalPower
|
||||||
|
|
||||||
#endif // CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_BROADCASTER
|
#endif // CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_BROADCASTER)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
|
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
|
||||||
* esp-nimble-cpp, NimBLE-Arduino contributors.
|
* esp-nimble-cpp, NimBLE-Arduino contributors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@@ -18,12 +18,13 @@
|
|||||||
#ifndef NIMBLE_CPP_BEACON_H_
|
#ifndef NIMBLE_CPP_BEACON_H_
|
||||||
#define NIMBLE_CPP_BEACON_H_
|
#define NIMBLE_CPP_BEACON_H_
|
||||||
|
|
||||||
#include "nimconfig.h"
|
#include "syscfg/syscfg.h"
|
||||||
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
|
#if CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_BROADCASTER)
|
||||||
|
|
||||||
class NimBLEUUID;
|
class NimBLEUUID;
|
||||||
|
|
||||||
# include <cstdint>
|
# include <cstdint>
|
||||||
|
# include <vector>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Representation of a beacon.
|
* @brief Representation of a beacon.
|
||||||
@@ -40,6 +41,10 @@ class NimBLEBeacon {
|
|||||||
uint16_t major{};
|
uint16_t major{};
|
||||||
uint16_t minor{};
|
uint16_t minor{};
|
||||||
int8_t signalPower{};
|
int8_t signalPower{};
|
||||||
|
operator std::vector<uint8_t> () const {
|
||||||
|
return std::vector<uint8_t>(reinterpret_cast<const uint8_t*>(this),
|
||||||
|
reinterpret_cast<const uint8_t*>(this) + sizeof(BeaconData));
|
||||||
|
}
|
||||||
} __attribute__((packed));
|
} __attribute__((packed));
|
||||||
|
|
||||||
const BeaconData& getData();
|
const BeaconData& getData();
|
||||||
@@ -60,5 +65,5 @@ class NimBLEBeacon {
|
|||||||
BeaconData m_beaconData;
|
BeaconData m_beaconData;
|
||||||
}; // NimBLEBeacon
|
}; // NimBLEBeacon
|
||||||
|
|
||||||
|
#endif // CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_PERIPHERAL)
|
||||||
#endif // NIMBLE_CPP_BEACON_H_
|
#endif // NIMBLE_CPP_BEACON_H_
|
||||||
#endif // CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
|
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
|
||||||
* esp-nimble-cpp, NimBLE-Arduino contributors.
|
* esp-nimble-cpp, NimBLE-Arduino contributors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@@ -15,10 +15,16 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "nimconfig.h"
|
#include "NimBLECharacteristic.h"
|
||||||
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
|
#if CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_PERIPHERAL)
|
||||||
|
|
||||||
|
# if defined(CONFIG_NIMBLE_CPP_IDF)
|
||||||
|
# if !defined(ESP_IDF_VERSION_MAJOR) || ESP_IDF_VERSION_MAJOR < 5
|
||||||
|
# define ble_gatts_notify_custom ble_gattc_notify_custom
|
||||||
|
# define ble_gatts_indicate_custom ble_gattc_indicate_custom
|
||||||
|
# endif
|
||||||
|
# endif
|
||||||
|
|
||||||
# include "NimBLECharacteristic.h"
|
|
||||||
# include "NimBLE2904.h"
|
# include "NimBLE2904.h"
|
||||||
# include "NimBLEDevice.h"
|
# include "NimBLEDevice.h"
|
||||||
# include "NimBLELog.h"
|
# include "NimBLELog.h"
|
||||||
@@ -159,21 +165,27 @@ void NimBLECharacteristic::removeDescriptor(NimBLEDescriptor* pDescriptor, bool
|
|||||||
/**
|
/**
|
||||||
* @brief Return the BLE Descriptor for the given UUID.
|
* @brief Return the BLE Descriptor for the given UUID.
|
||||||
* @param [in] uuid The UUID of the descriptor.
|
* @param [in] uuid The UUID of the descriptor.
|
||||||
|
* @param [in] index The index of the descriptor to return (used when multiple descriptors have the same UUID).
|
||||||
* @return A pointer to the descriptor object or nullptr if not found.
|
* @return A pointer to the descriptor object or nullptr if not found.
|
||||||
*/
|
*/
|
||||||
NimBLEDescriptor* NimBLECharacteristic::getDescriptorByUUID(const char* uuid) const {
|
NimBLEDescriptor* NimBLECharacteristic::getDescriptorByUUID(const char* uuid, uint16_t index) const {
|
||||||
return getDescriptorByUUID(NimBLEUUID(uuid));
|
return getDescriptorByUUID(NimBLEUUID(uuid), index);
|
||||||
} // getDescriptorByUUID
|
} // getDescriptorByUUID
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Return the BLE Descriptor for the given UUID.
|
* @brief Return the BLE Descriptor for the given UUID.
|
||||||
* @param [in] uuid The UUID of the descriptor.
|
* @param [in] uuid The UUID of the descriptor.
|
||||||
|
* @param [in] index The index of the descriptor to return (used when multiple descriptors have the same UUID).
|
||||||
* @return A pointer to the descriptor object or nullptr if not found.
|
* @return A pointer to the descriptor object or nullptr if not found.
|
||||||
*/
|
*/
|
||||||
NimBLEDescriptor* NimBLECharacteristic::getDescriptorByUUID(const NimBLEUUID& uuid) const {
|
NimBLEDescriptor* NimBLECharacteristic::getDescriptorByUUID(const NimBLEUUID& uuid, uint16_t index) const {
|
||||||
|
uint16_t position = 0;
|
||||||
for (const auto& dsc : m_vDescriptors) {
|
for (const auto& dsc : m_vDescriptors) {
|
||||||
if (dsc->getUUID() == uuid) {
|
if (dsc->getUUID() == uuid) {
|
||||||
return dsc;
|
if (position == index) {
|
||||||
|
return dsc;
|
||||||
|
}
|
||||||
|
position++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
@@ -219,7 +231,8 @@ void NimBLECharacteristic::setService(NimBLEService* pService) {
|
|||||||
* @return True if the indication was sent successfully, false otherwise.
|
* @return True if the indication was sent successfully, false otherwise.
|
||||||
*/
|
*/
|
||||||
bool NimBLECharacteristic::indicate(uint16_t connHandle) const {
|
bool NimBLECharacteristic::indicate(uint16_t connHandle) const {
|
||||||
return sendValue(nullptr, 0, false, connHandle);
|
auto value{m_value}; // make a copy to avoid issues if the value is changed while indicating
|
||||||
|
return sendValue(value.data(), value.size(), false, connHandle);
|
||||||
} // indicate
|
} // indicate
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -241,7 +254,8 @@ bool NimBLECharacteristic::indicate(const uint8_t* value, size_t length, uint16_
|
|||||||
* @return True if the notification was sent successfully, false otherwise.
|
* @return True if the notification was sent successfully, false otherwise.
|
||||||
*/
|
*/
|
||||||
bool NimBLECharacteristic::notify(uint16_t connHandle) const {
|
bool NimBLECharacteristic::notify(uint16_t connHandle) const {
|
||||||
return sendValue(nullptr, 0, true, connHandle);
|
auto value{m_value}; // make a copy to avoid issues if the value is changed while notifying
|
||||||
|
return sendValue(value.data(), value.size(), true, connHandle);
|
||||||
} // notify
|
} // notify
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -265,51 +279,159 @@ bool NimBLECharacteristic::notify(const uint8_t* value, size_t length, uint16_t
|
|||||||
* @return True if the value was sent successfully, false otherwise.
|
* @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 {
|
bool NimBLECharacteristic::sendValue(const uint8_t* value, size_t length, bool isNotification, uint16_t connHandle) const {
|
||||||
int rc = 0;
|
ble_npl_hw_enter_critical();
|
||||||
|
const auto subs = getSubscribers(); // make a copy to avoid issues if subscribers change while sending
|
||||||
|
ble_npl_hw_exit_critical(0);
|
||||||
|
|
||||||
if (value != nullptr && length > 0) { // custom notification value
|
bool chSpecified = connHandle != BLE_HS_CONN_HANDLE_NONE;
|
||||||
// Notify all connected peers unless a specific handle is provided
|
bool requireSecure = m_properties & (BLE_GATT_CHR_F_READ_ENC | BLE_GATT_CHR_F_READ_AUTHEN | BLE_GATT_CHR_F_READ_AUTHOR);
|
||||||
for (const auto& ch : NimBLEDevice::getServer()->getPeerDevices()) {
|
int rc = chSpecified ? BLE_HS_ENOENT : 0; // if handle specified, assume not found until sent
|
||||||
if (connHandle != BLE_HS_CONN_HANDLE_NONE && ch != connHandle) {
|
|
||||||
continue; // only send to the specific handle, minor inefficiency but saves code.
|
|
||||||
}
|
|
||||||
|
|
||||||
// Must re-create the data buffer on each iteration because it is freed by the calls bellow.
|
// Notify all connected peers unless a specific handle is provided
|
||||||
os_mbuf* om = ble_hs_mbuf_from_flat(value, length);
|
for (const auto& entry : subs) {
|
||||||
if (!om) {
|
uint16_t ch = entry.getConnHandle();
|
||||||
NIMBLE_LOGE(LOG_TAG, "<< sendValue: failed to allocate mbuf");
|
if (ch == BLE_HS_CONN_HANDLE_NONE || (chSpecified && ch != connHandle)) {
|
||||||
return false;
|
continue;
|
||||||
}
|
|
||||||
|
|
||||||
if (isNotification) {
|
|
||||||
rc = ble_gattc_notify_custom(ch, m_handle, om);
|
|
||||||
} else {
|
|
||||||
rc = ble_gattc_indicate_custom(ch, m_handle, om);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rc != 0) {
|
|
||||||
NIMBLE_LOGE(LOG_TAG, "<< sendValue: failed to send value, rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else if (connHandle != BLE_HS_CONN_HANDLE_NONE) { // only sending to specific peer
|
|
||||||
// Null buffer will read the value from the characteristic
|
if (requireSecure && !entry.isSecured()) {
|
||||||
|
NIMBLE_LOGW(LOG_TAG, "skipping notify/indicate to connHandle=%d, link not secured", entry.getConnHandle());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Must re-create the data buffer on each iteration because it is freed by the calls below.
|
||||||
|
uint8_t retries = 10; // wait up to 10ms for a free buffer
|
||||||
|
os_mbuf* om = ble_hs_mbuf_from_flat(value, length);
|
||||||
|
while (!om && --retries) {
|
||||||
|
ble_npl_time_delay(ble_npl_time_ms_to_ticks32(1));
|
||||||
|
om = ble_hs_mbuf_from_flat(value, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!om) {
|
||||||
|
rc = BLE_HS_ENOMEM;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (isNotification) {
|
if (isNotification) {
|
||||||
rc = ble_gattc_notify_custom(connHandle, m_handle, NULL);
|
rc = ble_gatts_notify_custom(ch, m_handle, om);
|
||||||
} else {
|
} else {
|
||||||
rc = ble_gattc_indicate_custom(connHandle, m_handle, NULL);
|
rc = ble_gatts_indicate_custom(ch, m_handle, om);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rc != 0 || chSpecified) {
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
} else { // Notify or indicate to all connected peers the characteristic value
|
|
||||||
ble_gatts_chr_updated(m_handle);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return rc == 0;
|
if (rc != 0) {
|
||||||
|
NIMBLE_LOGE(LOG_TAG, "failed to send value, rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
} // sendValue
|
} // sendValue
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Process a subscription or unsubscription request from a peer.
|
||||||
|
* @param[in] connInfo A reference to the connection info of the peer.
|
||||||
|
* @param[in] subVal The subscription value (bitmask).
|
||||||
|
*/
|
||||||
|
void NimBLECharacteristic::processSubRequest(NimBLEConnInfo& connInfo, uint8_t subVal) const {
|
||||||
|
// Only allocate subscribers for characteristics that support notify or indicate.
|
||||||
|
const uint16_t props = getProperties();
|
||||||
|
if (!(props & (BLE_GATT_CHR_F_NOTIFY | BLE_GATT_CHR_F_INDICATE))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto found = false;
|
||||||
|
auto firstFree = -1;
|
||||||
|
for (auto& entry : m_subPeers) {
|
||||||
|
if (entry.getConnHandle() == connInfo.getConnHandle()) {
|
||||||
|
found = true;
|
||||||
|
if (!subVal) {
|
||||||
|
ble_npl_hw_enter_critical();
|
||||||
|
entry = SubPeerEntry{}; // unsubscribed, reset entry
|
||||||
|
ble_npl_hw_exit_critical(0);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (firstFree == -1 && entry.getConnHandle() == BLE_HS_CONN_HANDLE_NONE) {
|
||||||
|
firstFree = &entry - &m_subPeers[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found && subVal) {
|
||||||
|
if (firstFree >= 0) {
|
||||||
|
ble_npl_hw_enter_critical();
|
||||||
|
m_subPeers[firstFree].setConnHandle(connInfo.getConnHandle());
|
||||||
|
m_subPeers[firstFree].setSubNotify(subVal & 0x1);
|
||||||
|
m_subPeers[firstFree].setSubIndicate(subVal & 0x2);
|
||||||
|
m_subPeers[firstFree].setSecured(connInfo.isEncrypted() || connInfo.isAuthenticated() || connInfo.isBonded());
|
||||||
|
if (m_properties & (BLE_GATT_CHR_F_READ_AUTHEN | BLE_GATT_CHR_F_READ_AUTHOR | BLE_GATT_CHR_F_READ_ENC)) {
|
||||||
|
// characteristic requires security/authorization
|
||||||
|
if (!m_subPeers[firstFree].isSecured()) {
|
||||||
|
m_subPeers[firstFree].setAwaitingSecure(true);
|
||||||
|
ble_npl_hw_exit_critical(0);
|
||||||
|
NimBLEDevice::startSecurity(connInfo.getConnHandle());
|
||||||
|
NIMBLE_LOGD(LOG_TAG,
|
||||||
|
"Subscription deferred until link is secured for connHandle=%d",
|
||||||
|
connInfo.getConnHandle());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ble_npl_hw_exit_critical(0);
|
||||||
|
} else {
|
||||||
|
// should never happen, but log just in case
|
||||||
|
NIMBLE_LOGE(LOG_TAG, "No free subscription slots");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_pCallbacks->onSubscribe(const_cast<NimBLECharacteristic*>(this), const_cast<NimBLEConnInfo&>(connInfo), subVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Update the security status of a subscribed peer.
|
||||||
|
* @param[in] peerInfo A reference to the connection info of the peer.
|
||||||
|
*/
|
||||||
|
void NimBLECharacteristic::updatePeerStatus(const NimBLEConnInfo& peerInfo) const {
|
||||||
|
if (!(getProperties() & (NIMBLE_PROPERTY::NOTIFY | NIMBLE_PROPERTY::INDICATE))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ble_npl_hw_enter_critical();
|
||||||
|
for (auto& entry : m_subPeers) {
|
||||||
|
if (entry.getConnHandle() == peerInfo.getConnHandle()) {
|
||||||
|
entry.setSecured(peerInfo.isEncrypted() || peerInfo.isAuthenticated() || peerInfo.isBonded());
|
||||||
|
if (entry.isAwaitingSecure()) {
|
||||||
|
entry.setAwaitingSecure(false);
|
||||||
|
ble_npl_hw_exit_critical(0);
|
||||||
|
m_pCallbacks->onSubscribe(const_cast<NimBLECharacteristic*>(this),
|
||||||
|
const_cast<NimBLEConnInfo&>(peerInfo),
|
||||||
|
entry.isSubNotify() | (entry.isSubIndicate() << 1));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ble_npl_hw_exit_critical(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Handle a read event from a client.
|
||||||
|
* @param [in] connInfo A reference to a NimBLEConnInfo instance containing the peer info.
|
||||||
|
*/
|
||||||
void NimBLECharacteristic::readEvent(NimBLEConnInfo& connInfo) {
|
void NimBLECharacteristic::readEvent(NimBLEConnInfo& connInfo) {
|
||||||
m_pCallbacks->onRead(this, connInfo);
|
m_pCallbacks->onRead(this, connInfo);
|
||||||
} // readEvent
|
} // readEvent
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Handle a write event from a client.
|
||||||
|
* @param [in] val A pointer to the data written by the client.
|
||||||
|
* @param [in] len The length of the data written by the client.
|
||||||
|
* @param [in] connInfo A reference to a NimBLEConnInfo instance containing the peer info.
|
||||||
|
*/
|
||||||
void NimBLECharacteristic::writeEvent(const uint8_t* val, uint16_t len, NimBLEConnInfo& connInfo) {
|
void NimBLECharacteristic::writeEvent(const uint8_t* val, uint16_t len, NimBLEConnInfo& connInfo) {
|
||||||
setValue(val, len);
|
setValue(val, len);
|
||||||
m_pCallbacks->onWrite(this, connInfo);
|
m_pCallbacks->onWrite(this, connInfo);
|
||||||
@@ -383,6 +505,18 @@ void NimBLECharacteristicCallbacks::onStatus(NimBLECharacteristic* pCharacterist
|
|||||||
NIMBLE_LOGD("NimBLECharacteristicCallbacks", "onStatus: default");
|
NIMBLE_LOGD("NimBLECharacteristicCallbacks", "onStatus: default");
|
||||||
} // onStatus
|
} // onStatus
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Callback function to support a Notify/Indicate Status report.
|
||||||
|
* @param [in] pCharacteristic The characteristic that is the source of the event.
|
||||||
|
* @param [in] connInfo A reference to a NimBLEConnInfo instance containing the peer info.
|
||||||
|
* @param [in] code Status return code from the NimBLE stack.
|
||||||
|
* @details The status code for success is 0 for notifications and BLE_HS_EDONE for indications,
|
||||||
|
* any other value is an error.
|
||||||
|
*/
|
||||||
|
void NimBLECharacteristicCallbacks::onStatus(NimBLECharacteristic* pCharacteristic, NimBLEConnInfo& connInfo, int code) {
|
||||||
|
NIMBLE_LOGD("NimBLECharacteristicCallbacks", "onStatus: default");
|
||||||
|
} // onStatus
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Callback function called when a client changes subscription status.
|
* @brief Callback function called when a client changes subscription status.
|
||||||
* @param [in] pCharacteristic The characteristic that is the source of the event.
|
* @param [in] pCharacteristic The characteristic that is the source of the event.
|
||||||
@@ -399,4 +533,4 @@ void NimBLECharacteristicCallbacks::onSubscribe(NimBLECharacteristic* pCharacter
|
|||||||
NIMBLE_LOGD("NimBLECharacteristicCallbacks", "onSubscribe: default");
|
NIMBLE_LOGD("NimBLECharacteristicCallbacks", "onSubscribe: default");
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL */
|
#endif // CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_PERIPHERAL)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
|
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
|
||||||
* esp-nimble-cpp, NimBLE-Arduino contributors.
|
* esp-nimble-cpp, NimBLE-Arduino contributors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@@ -17,8 +17,9 @@
|
|||||||
|
|
||||||
#ifndef NIMBLE_CPP_CHARACTERISTIC_H_
|
#ifndef NIMBLE_CPP_CHARACTERISTIC_H_
|
||||||
#define NIMBLE_CPP_CHARACTERISTIC_H_
|
#define NIMBLE_CPP_CHARACTERISTIC_H_
|
||||||
#include "nimconfig.h"
|
|
||||||
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
|
#include "syscfg/syscfg.h"
|
||||||
|
#if CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_PERIPHERAL)
|
||||||
|
|
||||||
class NimBLECharacteristicCallbacks;
|
class NimBLECharacteristicCallbacks;
|
||||||
class NimBLEService;
|
class NimBLEService;
|
||||||
@@ -30,6 +31,7 @@ class NimBLE2904;
|
|||||||
|
|
||||||
# include <string>
|
# include <string>
|
||||||
# include <vector>
|
# include <vector>
|
||||||
|
# include <array>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief The model of a BLE Characteristic.
|
* @brief The model of a BLE Characteristic.
|
||||||
@@ -67,8 +69,8 @@ class NimBLECharacteristic : public NimBLELocalValueAttribute {
|
|||||||
uint32_t properties = NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE,
|
uint32_t properties = NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE,
|
||||||
uint16_t maxLen = BLE_ATT_ATTR_MAX_LEN);
|
uint16_t maxLen = BLE_ATT_ATTR_MAX_LEN);
|
||||||
NimBLE2904* create2904();
|
NimBLE2904* create2904();
|
||||||
NimBLEDescriptor* getDescriptorByUUID(const char* uuid) const;
|
NimBLEDescriptor* getDescriptorByUUID(const char* uuid, uint16_t index = 0) const;
|
||||||
NimBLEDescriptor* getDescriptorByUUID(const NimBLEUUID& uuid) const;
|
NimBLEDescriptor* getDescriptorByUUID(const NimBLEUUID& uuid, uint16_t index = 0) const;
|
||||||
NimBLEDescriptor* getDescriptorByHandle(uint16_t handle) const;
|
NimBLEDescriptor* getDescriptorByHandle(uint16_t handle) const;
|
||||||
NimBLEService* getService() const;
|
NimBLEService* getService() const;
|
||||||
|
|
||||||
@@ -87,7 +89,9 @@ class NimBLECharacteristic : public NimBLELocalValueAttribute {
|
|||||||
# ifdef _DOXYGEN_
|
# ifdef _DOXYGEN_
|
||||||
bool
|
bool
|
||||||
# else
|
# else
|
||||||
typename std::enable_if<!std::is_pointer<T>::value && !Has_c_str_length<T>::value && !Has_data_size<T>::value, bool>::type
|
typename std::enable_if<!std::is_pointer<T>::value && !std::is_array<T>::value && !Has_c_str_length<T>::value &&
|
||||||
|
!Has_data_size<T>::value,
|
||||||
|
bool>::type
|
||||||
# endif
|
# endif
|
||||||
notify(const T& v, uint16_t connHandle = BLE_HS_CONN_HANDLE_NONE) const {
|
notify(const T& v, uint16_t connHandle = BLE_HS_CONN_HANDLE_NONE) const {
|
||||||
return notify(reinterpret_cast<const uint8_t*>(&v), sizeof(T), connHandle);
|
return notify(reinterpret_cast<const uint8_t*>(&v), sizeof(T), connHandle);
|
||||||
@@ -109,7 +113,23 @@ class NimBLECharacteristic : public NimBLELocalValueAttribute {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Template to send a notification with a value from a class that has a data() and size() method.
|
* @brief Template to send a notification with a value from a class that has a data() and size() method with value_type.
|
||||||
|
* @param [in] v The value to send.
|
||||||
|
* @param [in] connHandle Optional, a connection handle to send the notification to.
|
||||||
|
* @details Correctly calculates byte size for containers with multi-byte element types.
|
||||||
|
*/
|
||||||
|
template <typename T>
|
||||||
|
# ifdef _DOXYGEN_
|
||||||
|
bool
|
||||||
|
# else
|
||||||
|
typename std::enable_if<Has_data_size<T>::value && Has_value_type<T>::value, bool>::type
|
||||||
|
# endif
|
||||||
|
notify(const T& v, uint16_t connHandle = BLE_HS_CONN_HANDLE_NONE) const {
|
||||||
|
return notify(reinterpret_cast<const uint8_t*>(v.data()), v.size() * sizeof(typename T::value_type), connHandle);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Template to send a notification with a value from a class that has a data() and size() method without value_type.
|
||||||
* @param [in] v The value to send.
|
* @param [in] v The value to send.
|
||||||
* @param [in] connHandle Optional, a connection handle to send the notification to.
|
* @param [in] connHandle Optional, a connection handle to send the notification to.
|
||||||
*/
|
*/
|
||||||
@@ -117,7 +137,7 @@ class NimBLECharacteristic : public NimBLELocalValueAttribute {
|
|||||||
# ifdef _DOXYGEN_
|
# ifdef _DOXYGEN_
|
||||||
bool
|
bool
|
||||||
# else
|
# else
|
||||||
typename std::enable_if<Has_data_size<T>::value, bool>::type
|
typename std::enable_if<Has_data_size<T>::value && !Has_value_type<T>::value, bool>::type
|
||||||
# endif
|
# endif
|
||||||
notify(const T& v, uint16_t connHandle = BLE_HS_CONN_HANDLE_NONE) const {
|
notify(const T& v, uint16_t connHandle = BLE_HS_CONN_HANDLE_NONE) const {
|
||||||
return notify(reinterpret_cast<const uint8_t*>(v.data()), v.size(), connHandle);
|
return notify(reinterpret_cast<const uint8_t*>(v.data()), v.size(), connHandle);
|
||||||
@@ -133,7 +153,9 @@ class NimBLECharacteristic : public NimBLELocalValueAttribute {
|
|||||||
# ifdef _DOXYGEN_
|
# ifdef _DOXYGEN_
|
||||||
bool
|
bool
|
||||||
# else
|
# else
|
||||||
typename std::enable_if<!std::is_pointer<T>::value && !Has_c_str_length<T>::value && !Has_data_size<T>::value, bool>::type
|
typename std::enable_if<!std::is_pointer<T>::value && !std::is_array<T>::value && !Has_c_str_length<T>::value &&
|
||||||
|
!Has_data_size<T>::value,
|
||||||
|
bool>::type
|
||||||
# endif
|
# endif
|
||||||
indicate(const T& v, uint16_t connHandle = BLE_HS_CONN_HANDLE_NONE) const {
|
indicate(const T& v, uint16_t connHandle = BLE_HS_CONN_HANDLE_NONE) const {
|
||||||
return indicate(reinterpret_cast<const uint8_t*>(&v), sizeof(T), connHandle);
|
return indicate(reinterpret_cast<const uint8_t*>(&v), sizeof(T), connHandle);
|
||||||
@@ -155,7 +177,23 @@ class NimBLECharacteristic : public NimBLELocalValueAttribute {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Template to send a indication with a value from a class that has a data() and size() method.
|
* @brief Template to send a indication with a value from a class that has a data() and size() method with value_type.
|
||||||
|
* @param [in] v The value to send.
|
||||||
|
* @param [in] connHandle Optional, a connection handle to send the notification to.
|
||||||
|
* @details Correctly calculates byte size for containers with multi-byte element types.
|
||||||
|
*/
|
||||||
|
template <typename T>
|
||||||
|
# ifdef _DOXYGEN_
|
||||||
|
bool
|
||||||
|
# else
|
||||||
|
typename std::enable_if<Has_data_size<T>::value && Has_value_type<T>::value, bool>::type
|
||||||
|
# endif
|
||||||
|
indicate(const T& v, uint16_t connHandle = BLE_HS_CONN_HANDLE_NONE) const {
|
||||||
|
return indicate(reinterpret_cast<const uint8_t*>(v.data()), v.size() * sizeof(typename T::value_type), connHandle);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Template to send a indication with a value from a class that has a data() and size() method without value_type.
|
||||||
* @param [in] v The value to send.
|
* @param [in] v The value to send.
|
||||||
* @param [in] connHandle Optional, a connection handle to send the notification to.
|
* @param [in] connHandle Optional, a connection handle to send the notification to.
|
||||||
*/
|
*/
|
||||||
@@ -163,7 +201,7 @@ class NimBLECharacteristic : public NimBLELocalValueAttribute {
|
|||||||
# ifdef _DOXYGEN_
|
# ifdef _DOXYGEN_
|
||||||
bool
|
bool
|
||||||
# else
|
# else
|
||||||
typename std::enable_if<Has_data_size<T>::value, bool>::type
|
typename std::enable_if<Has_data_size<T>::value && !Has_value_type<T>::value, bool>::type
|
||||||
# endif
|
# endif
|
||||||
indicate(const T& v, uint16_t connHandle = BLE_HS_CONN_HANDLE_NONE) const {
|
indicate(const T& v, uint16_t connHandle = BLE_HS_CONN_HANDLE_NONE) const {
|
||||||
return indicate(reinterpret_cast<const uint8_t*>(v.data()), v.size(), connHandle);
|
return indicate(reinterpret_cast<const uint8_t*>(v.data()), v.size(), connHandle);
|
||||||
@@ -182,10 +220,16 @@ class NimBLECharacteristic : public NimBLELocalValueAttribute {
|
|||||||
* @note This function is only available if the type T is not a pointer.
|
* @note This function is only available if the type T is not a pointer.
|
||||||
*/
|
*/
|
||||||
template <typename T>
|
template <typename T>
|
||||||
typename std::enable_if<!std::is_pointer<T>::value, bool>::type notify(const T& value,
|
typename std::enable_if<!std::is_pointer<T>::value && !std::is_array<T>::value, bool>::type notify(
|
||||||
uint16_t connHandle = BLE_HS_CONN_HANDLE_NONE) const {
|
const T& value, uint16_t connHandle = BLE_HS_CONN_HANDLE_NONE) const {
|
||||||
if constexpr (Has_data_size<T>::value) {
|
if constexpr (Has_data_size<T>::value) {
|
||||||
return notify(reinterpret_cast<const uint8_t*>(value.data()), value.size(), connHandle);
|
if constexpr (Has_value_type<T>::value) {
|
||||||
|
return notify(reinterpret_cast<const uint8_t*>(value.data()),
|
||||||
|
value.size() * sizeof(typename T::value_type),
|
||||||
|
connHandle);
|
||||||
|
} else {
|
||||||
|
return notify(reinterpret_cast<const uint8_t*>(value.data()), value.size(), connHandle);
|
||||||
|
}
|
||||||
} else if constexpr (Has_c_str_length<T>::value) {
|
} else if constexpr (Has_c_str_length<T>::value) {
|
||||||
return notify(reinterpret_cast<const uint8_t*>(value.c_str()), value.length(), connHandle);
|
return notify(reinterpret_cast<const uint8_t*>(value.c_str()), value.length(), connHandle);
|
||||||
} else {
|
} else {
|
||||||
@@ -204,10 +248,16 @@ class NimBLECharacteristic : public NimBLELocalValueAttribute {
|
|||||||
* @note This function is only available if the type T is not a pointer.
|
* @note This function is only available if the type T is not a pointer.
|
||||||
*/
|
*/
|
||||||
template <typename T>
|
template <typename T>
|
||||||
typename std::enable_if<!std::is_pointer<T>::value, bool>::type indicate(
|
typename std::enable_if<!std::is_pointer<T>::value && !std::is_array<T>::value, bool>::type indicate(
|
||||||
const T& value, uint16_t connHandle = BLE_HS_CONN_HANDLE_NONE) const {
|
const T& value, uint16_t connHandle = BLE_HS_CONN_HANDLE_NONE) const {
|
||||||
if constexpr (Has_data_size<T>::value) {
|
if constexpr (Has_data_size<T>::value) {
|
||||||
return indicate(reinterpret_cast<const uint8_t*>(value.data()), value.size(), connHandle);
|
if constexpr (Has_value_type<T>::value) {
|
||||||
|
return indicate(reinterpret_cast<const uint8_t*>(value.data()),
|
||||||
|
value.size() * sizeof(typename T::value_type),
|
||||||
|
connHandle);
|
||||||
|
} else {
|
||||||
|
return indicate(reinterpret_cast<const uint8_t*>(value.data()), value.size(), connHandle);
|
||||||
|
}
|
||||||
} else if constexpr (Has_c_str_length<T>::value) {
|
} else if constexpr (Has_c_str_length<T>::value) {
|
||||||
return indicate(reinterpret_cast<const uint8_t*>(value.c_str()), value.length(), connHandle);
|
return indicate(reinterpret_cast<const uint8_t*>(value.c_str()), value.length(), connHandle);
|
||||||
} else {
|
} else {
|
||||||
@@ -228,9 +278,33 @@ class NimBLECharacteristic : public NimBLELocalValueAttribute {
|
|||||||
bool is_notification = true,
|
bool is_notification = true,
|
||||||
uint16_t connHandle = BLE_HS_CONN_HANDLE_NONE) const;
|
uint16_t connHandle = BLE_HS_CONN_HANDLE_NONE) const;
|
||||||
|
|
||||||
|
struct SubPeerEntry {
|
||||||
|
enum : uint8_t { AWAITING_SECURE = 1 << 0, SECURE = 1 << 1, SUB_NOTIFY = 1 << 2, SUB_INDICATE = 1 << 3 };
|
||||||
|
void setConnHandle(uint16_t connHandle) { m_connHandle = connHandle; }
|
||||||
|
uint16_t getConnHandle() const { return m_connHandle; }
|
||||||
|
void setAwaitingSecure(bool awaiting) { awaiting ? m_flags |= AWAITING_SECURE : m_flags &= ~AWAITING_SECURE; }
|
||||||
|
void setSecured(bool secure) { secure ? m_flags |= SECURE : m_flags &= ~SECURE; }
|
||||||
|
void setSubNotify(bool notify) { notify ? m_flags |= SUB_NOTIFY : m_flags &= ~SUB_NOTIFY; }
|
||||||
|
void setSubIndicate(bool indicate) { indicate ? m_flags |= SUB_INDICATE : m_flags &= ~SUB_INDICATE; }
|
||||||
|
bool isSubNotify() const { return m_flags & SUB_NOTIFY; }
|
||||||
|
bool isSubIndicate() const { return m_flags & SUB_INDICATE; }
|
||||||
|
bool isSecured() const { return m_flags & SECURE; }
|
||||||
|
bool isAwaitingSecure() const { return m_flags & AWAITING_SECURE; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint16_t m_connHandle{BLE_HS_CONN_HANDLE_NONE};
|
||||||
|
uint8_t m_flags{0};
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
using SubPeerArray = std::array<SubPeerEntry, MYNEWT_VAL(BLE_MAX_CONNECTIONS)>;
|
||||||
|
SubPeerArray getSubscribers() const { return m_subPeers; }
|
||||||
|
void processSubRequest(NimBLEConnInfo& connInfo, uint8_t subVal) const;
|
||||||
|
void updatePeerStatus(const NimBLEConnInfo& peerInfo) const;
|
||||||
|
|
||||||
NimBLECharacteristicCallbacks* m_pCallbacks{nullptr};
|
NimBLECharacteristicCallbacks* m_pCallbacks{nullptr};
|
||||||
NimBLEService* m_pService{nullptr};
|
NimBLEService* m_pService{nullptr};
|
||||||
std::vector<NimBLEDescriptor*> m_vDescriptors{};
|
std::vector<NimBLEDescriptor*> m_vDescriptors{};
|
||||||
|
mutable SubPeerArray m_subPeers{};
|
||||||
}; // NimBLECharacteristic
|
}; // NimBLECharacteristic
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -245,9 +319,10 @@ class NimBLECharacteristicCallbacks {
|
|||||||
virtual ~NimBLECharacteristicCallbacks() {}
|
virtual ~NimBLECharacteristicCallbacks() {}
|
||||||
virtual void onRead(NimBLECharacteristic* pCharacteristic, NimBLEConnInfo& connInfo);
|
virtual void onRead(NimBLECharacteristic* pCharacteristic, NimBLEConnInfo& connInfo);
|
||||||
virtual void onWrite(NimBLECharacteristic* pCharacteristic, NimBLEConnInfo& connInfo);
|
virtual void onWrite(NimBLECharacteristic* pCharacteristic, NimBLEConnInfo& connInfo);
|
||||||
virtual void onStatus(NimBLECharacteristic* pCharacteristic, int code);
|
virtual void onStatus(NimBLECharacteristic* pCharacteristic, int code); // deprecated
|
||||||
|
virtual void onStatus(NimBLECharacteristic* pCharacteristic, NimBLEConnInfo& connInfo, int code);
|
||||||
virtual void onSubscribe(NimBLECharacteristic* pCharacteristic, NimBLEConnInfo& connInfo, uint16_t subValue);
|
virtual void onSubscribe(NimBLECharacteristic* pCharacteristic, NimBLEConnInfo& connInfo, uint16_t subValue);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL */
|
#endif // CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_PERIPHERAL)
|
||||||
#endif /*NIMBLE_CPP_CHARACTERISTIC_H_*/
|
#endif // NIMBLE_CPP_CHARACTERISTIC_H_
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
|
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
|
||||||
* esp-nimble-cpp, NimBLE-Arduino contributors.
|
* esp-nimble-cpp, NimBLE-Arduino contributors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@@ -15,10 +15,9 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "nimconfig.h"
|
#include "NimBLEClient.h"
|
||||||
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
|
#if CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_CENTRAL)
|
||||||
|
|
||||||
# include "NimBLEClient.h"
|
|
||||||
# include "NimBLERemoteService.h"
|
# include "NimBLERemoteService.h"
|
||||||
# include "NimBLERemoteCharacteristic.h"
|
# include "NimBLERemoteCharacteristic.h"
|
||||||
# include "NimBLEDevice.h"
|
# include "NimBLEDevice.h"
|
||||||
@@ -69,7 +68,7 @@ NimBLEClient::NimBLEClient(const NimBLEAddress& peerAddress)
|
|||||||
m_terminateFailCount{0},
|
m_terminateFailCount{0},
|
||||||
m_asyncSecureAttempt{0},
|
m_asyncSecureAttempt{0},
|
||||||
m_config{},
|
m_config{},
|
||||||
# if CONFIG_BT_NIMBLE_EXT_ADV
|
# if MYNEWT_VAL(BLE_EXT_ADV)
|
||||||
m_phyMask{BLE_GAP_LE_PHY_1M_MASK | BLE_GAP_LE_PHY_2M_MASK | BLE_GAP_LE_PHY_CODED_MASK},
|
m_phyMask{BLE_GAP_LE_PHY_1M_MASK | BLE_GAP_LE_PHY_2M_MASK | BLE_GAP_LE_PHY_CODED_MASK},
|
||||||
# endif
|
# endif
|
||||||
m_connParams{16,
|
m_connParams{16,
|
||||||
@@ -126,6 +125,24 @@ size_t NimBLEClient::deleteService(const NimBLEUUID& uuid) {
|
|||||||
return m_svcVec.size();
|
return m_svcVec.size();
|
||||||
} // deleteService
|
} // deleteService
|
||||||
|
|
||||||
|
# if MYNEWT_VAL(BLE_ROLE_OBSERVER)
|
||||||
|
/**
|
||||||
|
* @brief Connect to an advertising device.
|
||||||
|
* @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 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 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 using the address of the last connected device, or the address\n
|
* @brief Connect to the BLE Server using the address of the last connected device, or the address\n
|
||||||
* passed to the constructor.
|
* passed to the constructor.
|
||||||
@@ -141,22 +158,6 @@ bool NimBLEClient::connect(bool deleteAttributes, bool asyncConnect, bool exchan
|
|||||||
return connect(m_peerAddress, deleteAttributes, asyncConnect, exchangeMTU);
|
return connect(m_peerAddress, deleteAttributes, asyncConnect, exchangeMTU);
|
||||||
} // connect
|
} // connect
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Connect to an advertising device.
|
|
||||||
* @param [in] device The device to connect to.
|
|
||||||
* @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(const NimBLEAdvertisedDevice* device, bool deleteAttributes, bool asyncConnect, bool exchangeMTU) {
|
|
||||||
NimBLEAddress address(device->getAddress());
|
|
||||||
return connect(address, deleteAttributes, asyncConnect, exchangeMTU);
|
|
||||||
} // connect
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Connect to a BLE Server by address.
|
* @brief Connect to a BLE Server by address.
|
||||||
* @param [in] address The address of the server.
|
* @param [in] address The address of the server.
|
||||||
@@ -203,7 +204,7 @@ bool NimBLEClient::connect(const NimBLEAddress& address, bool deleteAttributes,
|
|||||||
m_config.exchangeMTU = exchangeMTU;
|
m_config.exchangeMTU = exchangeMTU;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
# if CONFIG_BT_NIMBLE_EXT_ADV
|
# if MYNEWT_VAL(BLE_EXT_ADV)
|
||||||
rc = ble_gap_ext_connect(NimBLEDevice::m_ownAddrType,
|
rc = ble_gap_ext_connect(NimBLEDevice::m_ownAddrType,
|
||||||
peerAddr,
|
peerAddr,
|
||||||
m_connectTimeout,
|
m_connectTimeout,
|
||||||
@@ -227,10 +228,15 @@ bool NimBLEClient::connect(const NimBLEAddress& address, bool deleteAttributes,
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case BLE_HS_EBUSY:
|
case BLE_HS_EBUSY:
|
||||||
|
# if MYNEWT_VAL(BLE_ROLE_OBSERVER)
|
||||||
|
|
||||||
// Scan was active, stop it through the NimBLEScan API to release any tasks and call the callback.
|
// Scan was active, stop it through the NimBLEScan API to release any tasks and call the callback.
|
||||||
if (!NimBLEDevice::getScan()->stop()) {
|
if (!NimBLEDevice::getScan()->stop()) {
|
||||||
rc = BLE_HS_EUNKNOWN;
|
rc = BLE_HS_EUNKNOWN;
|
||||||
}
|
}
|
||||||
|
# else
|
||||||
|
rc = BLE_HS_EUNKNOWN;
|
||||||
|
# endif
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case BLE_HS_EDONE:
|
case BLE_HS_EDONE:
|
||||||
@@ -396,7 +402,7 @@ void NimBLEClient::setConfig(NimBLEClient::Config config) {
|
|||||||
m_config = config;
|
m_config = config;
|
||||||
} // setConfig
|
} // setConfig
|
||||||
|
|
||||||
# if CONFIG_BT_NIMBLE_EXT_ADV
|
# if MYNEWT_VAL(BLE_EXT_ADV)
|
||||||
/**
|
/**
|
||||||
* @brief Set the PHY types to use when connecting to a server.
|
* @brief Set the PHY types to use when connecting to a server.
|
||||||
* @param [in] mask A bitmask indicating what PHYS to connect with.\n
|
* @param [in] mask A bitmask indicating what PHYS to connect with.\n
|
||||||
@@ -408,6 +414,7 @@ void NimBLEClient::setConfig(NimBLEClient::Config config) {
|
|||||||
void NimBLEClient::setConnectPhy(uint8_t mask) {
|
void NimBLEClient::setConnectPhy(uint8_t mask) {
|
||||||
m_phyMask = mask;
|
m_phyMask = mask;
|
||||||
} // setConnectPhy
|
} // setConnectPhy
|
||||||
|
# endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Request a change to the PHY used for this peer connection.
|
* @brief Request a change to the PHY used for this peer connection.
|
||||||
@@ -450,7 +457,6 @@ bool NimBLEClient::getPhy(uint8_t* txPhy, uint8_t* rxPhy) {
|
|||||||
|
|
||||||
return rc == 0;
|
return rc == 0;
|
||||||
} // getPhy
|
} // getPhy
|
||||||
# endif
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Set the connection parameters to use when connecting to a server.
|
* @brief Set the connection parameters to use when connecting to a server.
|
||||||
@@ -928,8 +934,18 @@ int NimBLEClient::handleGapEvent(struct ble_gap_event* event, void* arg) {
|
|||||||
switch (event->type) {
|
switch (event->type) {
|
||||||
case BLE_GAP_EVENT_DISCONNECT: {
|
case BLE_GAP_EVENT_DISCONNECT: {
|
||||||
// workaround for bug in NimBLE stack where disconnect event argument is not passed correctly
|
// workaround for bug in NimBLE stack where disconnect event argument is not passed correctly
|
||||||
pClient = NimBLEDevice::getClientByHandle(event->disconnect.conn.conn_handle);
|
pClient = NimBLEDevice::getClientByPeerAddress(event->disconnect.conn.peer_ota_addr);
|
||||||
if (pClient == nullptr) {
|
if (pClient == nullptr) {
|
||||||
|
pClient = NimBLEDevice::getClientByPeerAddress(event->disconnect.conn.peer_id_addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// try by connection handle
|
||||||
|
if (pClient == nullptr) {
|
||||||
|
pClient = NimBLEDevice::getClientByHandle(event->disconnect.conn.conn_handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pClient == nullptr) {
|
||||||
|
NIMBLE_LOGE(LOG_TAG, "Disconnected client not found, conn_handle=%d", event->disconnect.conn.conn_handle);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -954,7 +970,9 @@ int NimBLEClient::handleGapEvent(struct ble_gap_event* event, void* arg) {
|
|||||||
pClient->m_asyncSecureAttempt = 0;
|
pClient->m_asyncSecureAttempt = 0;
|
||||||
|
|
||||||
// Don't call the disconnect callback if we are waiting for a connection to complete and it fails
|
// 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) {
|
if (rc == (BLE_HS_ERR_HCI_BASE + BLE_ERR_CONN_ESTABLISHMENT) && pClient->m_config.asyncConnect) {
|
||||||
|
pClient->m_pClientCallbacks->onConnectFail(pClient, rc);
|
||||||
|
} else {
|
||||||
pClient->m_pClientCallbacks->onDisconnect(pClient, rc);
|
pClient->m_pClientCallbacks->onDisconnect(pClient, rc);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -982,6 +1000,10 @@ int NimBLEClient::handleGapEvent(struct ble_gap_event* event, void* arg) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
rc = event->connect.status;
|
rc = event->connect.status;
|
||||||
|
if (rc == BLE_ERR_UNSUPP_REM_FEATURE) {
|
||||||
|
rc = 0; // Workaround: Ignore unsupported remote feature error as it is not a real error.
|
||||||
|
}
|
||||||
|
|
||||||
if (rc == 0) {
|
if (rc == 0) {
|
||||||
pClient->m_connHandle = event->connect.conn_handle;
|
pClient->m_connHandle = event->connect.conn_handle;
|
||||||
|
|
||||||
@@ -1026,33 +1048,46 @@ int NimBLEClient::handleGapEvent(struct ble_gap_event* event, void* arg) {
|
|||||||
} // BLE_GAP_EVENT_TERM_FAILURE
|
} // BLE_GAP_EVENT_TERM_FAILURE
|
||||||
|
|
||||||
case BLE_GAP_EVENT_NOTIFY_RX: {
|
case BLE_GAP_EVENT_NOTIFY_RX: {
|
||||||
if (pClient->m_connHandle != event->notify_rx.conn_handle) return 0;
|
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);
|
NIMBLE_LOGD(LOG_TAG, "Notify Received for handle: %d", event->notify_rx.attr_handle);
|
||||||
|
|
||||||
for (const auto& svc : pClient->m_svcVec) {
|
NimBLERemoteCharacteristic* pChr = pClient->getCharacteristic(event->notify_rx.attr_handle);
|
||||||
// Dont waste cycles searching services without this handle in its range
|
if (pChr == nullptr) {
|
||||||
if (svc->getEndHandle() < event->notify_rx.attr_handle) {
|
NIMBLE_LOGW(LOG_TAG, "unknown handle: %d", event->notify_rx.attr_handle);
|
||||||
continue;
|
return BLE_ATT_ERR_INVALID_HANDLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
NIMBLE_LOGD(LOG_TAG,
|
auto len = event->notify_rx.om->om_len;
|
||||||
"checking service %s for handle: %d",
|
if (pChr->m_value.setValue(event->notify_rx.om->om_data, len)) {
|
||||||
svc->getUUID().toString().c_str(),
|
os_mbuf* next;
|
||||||
event->notify_rx.attr_handle);
|
next = SLIST_NEXT(event->notify_rx.om, om_next);
|
||||||
|
while (next != NULL) {
|
||||||
for (const auto& chr : svc->m_vChars) {
|
pChr->m_value.append(next->om_data, next->om_len);
|
||||||
if (chr->getHandle() == event->notify_rx.attr_handle) {
|
if (pChr->m_value.length() != len + next->om_len) {
|
||||||
NIMBLE_LOGD(LOG_TAG, "Got Notification for characteristic %s", chr->toString().c_str());
|
rc = BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
|
||||||
|
|
||||||
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;
|
break;
|
||||||
}
|
}
|
||||||
|
len += next->om_len;
|
||||||
|
next = SLIST_NEXT(next, om_next);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
rc = BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rc != 0) { // This should never happen
|
||||||
|
NIMBLE_LOGE(LOG_TAG, "notification value error; exceeds limit");
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pChr->m_notifyCallback != nullptr) {
|
||||||
|
// TODO: change this callback to use the NimBLEAttValue class instead of raw data and length
|
||||||
|
pChr->m_notifyCallback(pChr,
|
||||||
|
const_cast<uint8_t*>(pChr->m_value.getValue().data()),
|
||||||
|
pChr->m_value.length(),
|
||||||
|
!event->notify_rx.indication);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@@ -1141,7 +1176,6 @@ int NimBLEClient::handleGapEvent(struct ble_gap_event* event, void* arg) {
|
|||||||
break;
|
break;
|
||||||
} // BLE_GAP_EVENT_IDENTITY_RESOLVED
|
} // BLE_GAP_EVENT_IDENTITY_RESOLVED
|
||||||
|
|
||||||
# if CONFIG_BT_NIMBLE_EXT_ADV
|
|
||||||
case BLE_GAP_EVENT_PHY_UPDATE_COMPLETE: {
|
case BLE_GAP_EVENT_PHY_UPDATE_COMPLETE: {
|
||||||
NimBLEConnInfo peerInfo;
|
NimBLEConnInfo peerInfo;
|
||||||
rc = ble_gap_conn_find(event->phy_updated.conn_handle, &peerInfo.m_desc);
|
rc = ble_gap_conn_find(event->phy_updated.conn_handle, &peerInfo.m_desc);
|
||||||
@@ -1152,7 +1186,6 @@ int NimBLEClient::handleGapEvent(struct ble_gap_event* event, void* arg) {
|
|||||||
pClient->m_pClientCallbacks->onPhyUpdate(pClient, event->phy_updated.tx_phy, event->phy_updated.rx_phy);
|
pClient->m_pClientCallbacks->onPhyUpdate(pClient, event->phy_updated.tx_phy, event->phy_updated.rx_phy);
|
||||||
return 0;
|
return 0;
|
||||||
} // BLE_GAP_EVENT_PHY_UPDATE_COMPLETE
|
} // BLE_GAP_EVENT_PHY_UPDATE_COMPLETE
|
||||||
# endif
|
|
||||||
|
|
||||||
case BLE_GAP_EVENT_MTU: {
|
case BLE_GAP_EVENT_MTU: {
|
||||||
if (pClient->m_connHandle != event->mtu.conn_handle) {
|
if (pClient->m_connHandle != event->mtu.conn_handle) {
|
||||||
@@ -1177,7 +1210,16 @@ int NimBLEClient::handleGapEvent(struct ble_gap_event* event, void* arg) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event->passkey.params.action == BLE_SM_IOACT_NUMCMP) {
|
if (event->passkey.params.action == BLE_SM_IOACT_DISP) {
|
||||||
|
struct ble_sm_io pkey = {0, 0};
|
||||||
|
pkey.action = event->passkey.params.action;
|
||||||
|
pkey.passkey = NimBLEDevice::getSecurityPasskey();
|
||||||
|
if (pkey.passkey == 123456) {
|
||||||
|
pkey.passkey = pClient->m_pClientCallbacks->onPassKeyDisplay(peerInfo);
|
||||||
|
}
|
||||||
|
rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey);
|
||||||
|
NIMBLE_LOGD(LOG_TAG, "BLE_SM_IOACT_DISP; ble_sm_inject_io result: %d", rc);
|
||||||
|
} else if (event->passkey.params.action == BLE_SM_IOACT_NUMCMP) {
|
||||||
NIMBLE_LOGD(LOG_TAG, "Passkey on device's display: %" PRIu32, event->passkey.params.numcmp);
|
NIMBLE_LOGD(LOG_TAG, "Passkey on device's display: %" PRIu32, event->passkey.params.numcmp);
|
||||||
pClient->m_pClientCallbacks->onConfirmPasskey(peerInfo, event->passkey.params.numcmp);
|
pClient->m_pClientCallbacks->onConfirmPasskey(peerInfo, event->passkey.params.numcmp);
|
||||||
} else if (event->passkey.params.action == BLE_SM_IOACT_OOB) {
|
} else if (event->passkey.params.action == BLE_SM_IOACT_OOB) {
|
||||||
@@ -1281,6 +1323,11 @@ void NimBLEClientCallbacks::onPassKeyEntry(NimBLEConnInfo& connInfo) {
|
|||||||
NimBLEDevice::injectPassKey(connInfo, 123456);
|
NimBLEDevice::injectPassKey(connInfo, 123456);
|
||||||
} // onPassKeyEntry
|
} // onPassKeyEntry
|
||||||
|
|
||||||
|
uint32_t NimBLEClientCallbacks::onPassKeyDisplay(NimBLEConnInfo& connInfo) {
|
||||||
|
NIMBLE_LOGD(CB_TAG, "onPassKeyDisplay: default");
|
||||||
|
return NimBLEDevice::getSecurityPasskey();
|
||||||
|
} // onPassKeyDisplay
|
||||||
|
|
||||||
void NimBLEClientCallbacks::onAuthenticationComplete(NimBLEConnInfo& connInfo) {
|
void NimBLEClientCallbacks::onAuthenticationComplete(NimBLEConnInfo& connInfo) {
|
||||||
NIMBLE_LOGD(CB_TAG, "onAuthenticationComplete: default");
|
NIMBLE_LOGD(CB_TAG, "onAuthenticationComplete: default");
|
||||||
} // onAuthenticationComplete
|
} // onAuthenticationComplete
|
||||||
@@ -1298,10 +1345,9 @@ void NimBLEClientCallbacks::onMTUChange(NimBLEClient* pClient, uint16_t mtu) {
|
|||||||
NIMBLE_LOGD(CB_TAG, "onMTUChange: default");
|
NIMBLE_LOGD(CB_TAG, "onMTUChange: default");
|
||||||
} // onMTUChange
|
} // onMTUChange
|
||||||
|
|
||||||
# if CONFIG_BT_NIMBLE_EXT_ADV
|
|
||||||
void NimBLEClientCallbacks::onPhyUpdate(NimBLEClient* pClient, uint8_t txPhy, uint8_t rxPhy) {
|
void NimBLEClientCallbacks::onPhyUpdate(NimBLEClient* pClient, uint8_t txPhy, uint8_t rxPhy) {
|
||||||
NIMBLE_LOGD(CB_TAG, "onPhyUpdate: default, txPhy: %d, rxPhy: %d", txPhy, rxPhy);
|
NIMBLE_LOGD(CB_TAG, "onPhyUpdate: default, txPhy: %d, rxPhy: %d", txPhy, rxPhy);
|
||||||
} // onPhyUpdate
|
} // onPhyUpdate
|
||||||
# endif
|
#
|
||||||
|
|
||||||
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL */
|
#endif // CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_CENTRAL)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
|
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
|
||||||
* esp-nimble-cpp, NimBLE-Arduino contributors.
|
* esp-nimble-cpp, NimBLE-Arduino contributors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@@ -18,8 +18,8 @@
|
|||||||
#ifndef NIMBLE_CPP_CLIENT_H_
|
#ifndef NIMBLE_CPP_CLIENT_H_
|
||||||
#define NIMBLE_CPP_CLIENT_H_
|
#define NIMBLE_CPP_CLIENT_H_
|
||||||
|
|
||||||
#include "nimconfig.h"
|
#include "syscfg/syscfg.h"
|
||||||
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
|
#if CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_CENTRAL)
|
||||||
|
|
||||||
# if defined(CONFIG_NIMBLE_CPP_IDF)
|
# if defined(CONFIG_NIMBLE_CPP_IDF)
|
||||||
# include "host/ble_gap.h"
|
# include "host/ble_gap.h"
|
||||||
@@ -48,10 +48,12 @@ struct NimBLETaskData;
|
|||||||
*/
|
*/
|
||||||
class NimBLEClient {
|
class NimBLEClient {
|
||||||
public:
|
public:
|
||||||
|
# if MYNEWT_VAL(BLE_ROLE_OBSERVER)
|
||||||
bool connect(const NimBLEAdvertisedDevice* device,
|
bool connect(const NimBLEAdvertisedDevice* device,
|
||||||
bool deleteAttributes = true,
|
bool deleteAttributes = true,
|
||||||
bool asyncConnect = false,
|
bool asyncConnect = false,
|
||||||
bool exchangeMTU = true);
|
bool exchangeMTU = true);
|
||||||
|
# endif
|
||||||
bool connect(const NimBLEAddress& address, bool deleteAttributes = true, bool asyncConnect = false, bool exchangeMTU = true);
|
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 connect(bool deleteAttributes = true, bool asyncConnect = false, bool exchangeMTU = true);
|
||||||
bool disconnect(uint8_t reason = BLE_ERR_REM_USER_CONN_TERM);
|
bool disconnect(uint8_t reason = BLE_ERR_REM_USER_CONN_TERM);
|
||||||
@@ -93,11 +95,11 @@ class NimBLEClient {
|
|||||||
const NimBLEAttValue& value,
|
const NimBLEAttValue& value,
|
||||||
bool response = false);
|
bool response = false);
|
||||||
|
|
||||||
# if CONFIG_BT_NIMBLE_EXT_ADV
|
# if MYNEWT_VAL(BLE_EXT_ADV)
|
||||||
void setConnectPhy(uint8_t phyMask);
|
void setConnectPhy(uint8_t phyMask);
|
||||||
|
# endif
|
||||||
bool updatePhy(uint8_t txPhysMask, uint8_t rxPhysMask, uint16_t phyOptions = 0);
|
bool updatePhy(uint8_t txPhysMask, uint8_t rxPhysMask, uint16_t phyOptions = 0);
|
||||||
bool getPhy(uint8_t* txPhy, uint8_t* rxPhy);
|
bool getPhy(uint8_t* txPhy, uint8_t* rxPhy);
|
||||||
# endif
|
|
||||||
|
|
||||||
struct Config {
|
struct Config {
|
||||||
uint8_t deleteCallbacks : 1; // Delete the callback object when the client is deleted.
|
uint8_t deleteCallbacks : 1; // Delete the callback object when the client is deleted.
|
||||||
@@ -135,7 +137,7 @@ class NimBLEClient {
|
|||||||
mutable uint8_t m_asyncSecureAttempt;
|
mutable uint8_t m_asyncSecureAttempt;
|
||||||
Config m_config;
|
Config m_config;
|
||||||
|
|
||||||
# if CONFIG_BT_NIMBLE_EXT_ADV
|
# if MYNEWT_VAL(BLE_EXT_ADV)
|
||||||
uint8_t m_phyMask;
|
uint8_t m_phyMask;
|
||||||
# endif
|
# endif
|
||||||
ble_gap_conn_params m_connParams;
|
ble_gap_conn_params m_connParams;
|
||||||
@@ -185,6 +187,13 @@ class NimBLEClientCallbacks {
|
|||||||
*/
|
*/
|
||||||
virtual void onPassKeyEntry(NimBLEConnInfo& connInfo);
|
virtual void onPassKeyEntry(NimBLEConnInfo& connInfo);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Called when using passkey entry pairing and the passkey should be displayed.
|
||||||
|
* @param [in] connInfo A reference to a NimBLEConnInfo instance containing the peer info.
|
||||||
|
* @return The passkey to display to the user. The peer device must enter this passkey to complete the pairing.
|
||||||
|
*/
|
||||||
|
virtual uint32_t onPassKeyDisplay(NimBLEConnInfo& connInfo);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Called when the pairing procedure is complete.
|
* @brief Called when the pairing procedure is complete.
|
||||||
* @param [in] connInfo A reference to a NimBLEConnInfo instance containing the peer info.\n
|
* @param [in] connInfo A reference to a NimBLEConnInfo instance containing the peer info.\n
|
||||||
@@ -213,7 +222,6 @@ class NimBLEClientCallbacks {
|
|||||||
*/
|
*/
|
||||||
virtual void onMTUChange(NimBLEClient* pClient, uint16_t MTU);
|
virtual void onMTUChange(NimBLEClient* pClient, uint16_t MTU);
|
||||||
|
|
||||||
# if CONFIG_BT_NIMBLE_EXT_ADV
|
|
||||||
/**
|
/**
|
||||||
* @brief Called when the PHY update procedure is complete.
|
* @brief Called when the PHY update procedure is complete.
|
||||||
* @param [in] pClient A pointer to the client whose PHY was updated.
|
* @param [in] pClient A pointer to the client whose PHY was updated.
|
||||||
@@ -226,8 +234,7 @@ class NimBLEClientCallbacks {
|
|||||||
* * BLE_GAP_LE_PHY_CODED
|
* * BLE_GAP_LE_PHY_CODED
|
||||||
*/
|
*/
|
||||||
virtual void onPhyUpdate(NimBLEClient* pClient, uint8_t txPhy, uint8_t rxPhy);
|
virtual void onPhyUpdate(NimBLEClient* pClient, uint8_t txPhy, uint8_t rxPhy);
|
||||||
# endif
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL */
|
#endif // CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_CENTRAL)
|
||||||
#endif /* NIMBLE_CPP_CLIENT_H_ */
|
#endif // NIMBLE_CPP_CLIENT_H_
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
|
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
|
||||||
* esp-nimble-cpp, NimBLE-Arduino contributors.
|
* esp-nimble-cpp, NimBLE-Arduino contributors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@@ -15,8 +15,8 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef NIMBLECONNINFO_H_
|
#ifndef NIMBLE_CPP_CONNINFO_H_
|
||||||
#define NIMBLECONNINFO_H_
|
#define NIMBLE_CPP_CONNINFO_H_
|
||||||
|
|
||||||
#if defined(CONFIG_NIMBLE_CPP_IDF)
|
#if defined(CONFIG_NIMBLE_CPP_IDF)
|
||||||
# include "host/ble_gap.h"
|
# include "host/ble_gap.h"
|
||||||
@@ -25,6 +25,7 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "NimBLEAddress.h"
|
#include "NimBLEAddress.h"
|
||||||
|
#include <cstdio>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Connection information.
|
* @brief Connection information.
|
||||||
@@ -70,6 +71,41 @@ class NimBLEConnInfo {
|
|||||||
/** @brief Gets the key size used to encrypt the connection */
|
/** @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; }
|
||||||
|
|
||||||
|
/** @brief Get a string representation of the connection info, useful for debugging */
|
||||||
|
std::string toString() const {
|
||||||
|
std::string str;
|
||||||
|
// 294 chars max expected from all labels + worst-case values, round up to 300.
|
||||||
|
str.resize(300);
|
||||||
|
|
||||||
|
snprintf(&str[0],
|
||||||
|
str.size(),
|
||||||
|
" Address: %s\n"
|
||||||
|
" ID Address: %s\n"
|
||||||
|
" Connection Handle: %u\n"
|
||||||
|
" Connection Interval: %.1f ms\n"
|
||||||
|
" Connection Timeout: %u ms\n"
|
||||||
|
" Connection Latency: %u\n"
|
||||||
|
" MTU: %u bytes\n"
|
||||||
|
" Role: %s\n"
|
||||||
|
" Bonded: %s\n"
|
||||||
|
" Encrypted: %s\n"
|
||||||
|
" Authenticated: %s\n"
|
||||||
|
" Security Key Size: %u\n",
|
||||||
|
getAddress().toString().c_str(),
|
||||||
|
getIdAddress().toString().c_str(),
|
||||||
|
getConnHandle(),
|
||||||
|
getConnInterval() * 1.25f,
|
||||||
|
getConnTimeout() * 10,
|
||||||
|
getConnLatency(),
|
||||||
|
getMTU(),
|
||||||
|
isMaster() ? "Master" : "Slave",
|
||||||
|
isBonded() ? "Yes" : "No",
|
||||||
|
isEncrypted() ? "Yes" : "No",
|
||||||
|
isAuthenticated() ? "Yes" : "No",
|
||||||
|
getSecKeySize());
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class NimBLEServer;
|
friend class NimBLEServer;
|
||||||
friend class NimBLEClient;
|
friend class NimBLEClient;
|
||||||
@@ -80,4 +116,5 @@ class NimBLEConnInfo {
|
|||||||
NimBLEConnInfo() {};
|
NimBLEConnInfo() {};
|
||||||
NimBLEConnInfo(ble_gap_conn_desc desc) { m_desc = desc; }
|
NimBLEConnInfo(ble_gap_conn_desc desc) { m_desc = desc; }
|
||||||
};
|
};
|
||||||
#endif
|
|
||||||
|
#endif // NIMBLE_CPP_CONNINFO_H_
|
||||||
|
|||||||
80
src/NimBLECppVersion.h
Normal file
80
src/NimBLECppVersion.h
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
|
||||||
|
* esp-nimble-cpp, NimBLE-Arduino contributors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef NIMBLE_CPP_VERSION_H_
|
||||||
|
#define NIMBLE_CPP_VERSION_H_
|
||||||
|
|
||||||
|
/** @brief NimBLE-Arduino library major version number. */
|
||||||
|
#define NIMBLE_CPP_VERSION_MAJOR 2
|
||||||
|
|
||||||
|
/** @brief NimBLE-Arduino library minor version number. */
|
||||||
|
#define NIMBLE_CPP_VERSION_MINOR 4
|
||||||
|
|
||||||
|
/** @brief NimBLE-Arduino library patch version number. */
|
||||||
|
#define NIMBLE_CPP_VERSION_PATCH 0
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Macro to create a version number for comparison.
|
||||||
|
* @param major Major version number.
|
||||||
|
* @param minor Minor version number.
|
||||||
|
* @param patch Patch version number.
|
||||||
|
* @details Example usage:
|
||||||
|
* @code{.cpp}
|
||||||
|
* #if NIMBLE_CPP_VERSION >= NIMBLE_CPP_VERSION_VAL(2, 0, 0)
|
||||||
|
* // Using NimBLE-Arduino v2 or later
|
||||||
|
* #endif
|
||||||
|
* @endcode
|
||||||
|
*/
|
||||||
|
#define NIMBLE_CPP_VERSION_VAL(major, minor, patch) (((major) << 16) | ((minor) << 8) | (patch))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The library version as a single integer for compile-time comparison.
|
||||||
|
* @details Format: (major << 16) | (minor << 8) | patch
|
||||||
|
*/
|
||||||
|
#define NIMBLE_CPP_VERSION \
|
||||||
|
NIMBLE_CPP_VERSION_VAL(NIMBLE_CPP_VERSION_MAJOR, NIMBLE_CPP_VERSION_MINOR, NIMBLE_CPP_VERSION_PATCH)
|
||||||
|
|
||||||
|
/** @cond NIMBLE_CPP_INTERNAL */
|
||||||
|
#define NIMBLE_CPP_VERSION_STRINGIFY_IMPL(x) #x
|
||||||
|
#define NIMBLE_CPP_VERSION_STRINGIFY(x) NIMBLE_CPP_VERSION_STRINGIFY_IMPL(x)
|
||||||
|
/** @endcond */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Optional Semantic Versioning prerelease suffix.
|
||||||
|
* @details Include the leading '-' when defined, for example: "-beta.1"
|
||||||
|
*/
|
||||||
|
#ifndef NIMBLE_CPP_VERSION_PRERELEASE
|
||||||
|
# define NIMBLE_CPP_VERSION_PRERELEASE ""
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Optional Semantic Versioning build metadata suffix.
|
||||||
|
* @details Include the leading '+' when defined, for example: "+sha.abcd1234"
|
||||||
|
*/
|
||||||
|
#ifndef NIMBLE_CPP_VERSION_BUILD_METADATA
|
||||||
|
# define NIMBLE_CPP_VERSION_BUILD_METADATA ""
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** @brief library version as a prefixed Semantic Versioning string. */
|
||||||
|
#define NIMBLE_CPP_VERSION_STR \
|
||||||
|
"NimBLE-CPP " \
|
||||||
|
NIMBLE_CPP_VERSION_STRINGIFY(NIMBLE_CPP_VERSION_MAJOR) "." \
|
||||||
|
NIMBLE_CPP_VERSION_STRINGIFY(NIMBLE_CPP_VERSION_MINOR) "." \
|
||||||
|
NIMBLE_CPP_VERSION_STRINGIFY(NIMBLE_CPP_VERSION_PATCH) \
|
||||||
|
NIMBLE_CPP_VERSION_PRERELEASE NIMBLE_CPP_VERSION_BUILD_METADATA
|
||||||
|
|
||||||
|
#endif // NIMBLE_CPP_VERSION_H_
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
|
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
|
||||||
* esp-nimble-cpp, NimBLE-Arduino contributors.
|
* esp-nimble-cpp, NimBLE-Arduino contributors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@@ -15,11 +15,10 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "nimconfig.h"
|
#include "NimBLEDescriptor.h"
|
||||||
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
|
#if CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_PERIPHERAL)
|
||||||
|
|
||||||
# include "NimBLEService.h"
|
# include "NimBLEService.h"
|
||||||
# include "NimBLEDescriptor.h"
|
|
||||||
# include "NimBLELog.h"
|
# include "NimBLELog.h"
|
||||||
|
|
||||||
# include <string>
|
# include <string>
|
||||||
@@ -148,4 +147,4 @@ void NimBLEDescriptorCallbacks::onWrite(NimBLEDescriptor* pDescriptor, NimBLECon
|
|||||||
NIMBLE_LOGD("NimBLEDescriptorCallbacks", "onWrite: default");
|
NIMBLE_LOGD("NimBLEDescriptorCallbacks", "onWrite: default");
|
||||||
} // onWrite
|
} // onWrite
|
||||||
|
|
||||||
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL */
|
#endif // CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_PERIPHERAL)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
|
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
|
||||||
* esp-nimble-cpp, NimBLE-Arduino contributors.
|
* esp-nimble-cpp, NimBLE-Arduino contributors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@@ -18,20 +18,15 @@
|
|||||||
#ifndef NIMBLE_CPP_DESCRIPTOR_H_
|
#ifndef NIMBLE_CPP_DESCRIPTOR_H_
|
||||||
#define NIMBLE_CPP_DESCRIPTOR_H_
|
#define NIMBLE_CPP_DESCRIPTOR_H_
|
||||||
|
|
||||||
#include "nimconfig.h"
|
#include "syscfg/syscfg.h"
|
||||||
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
|
#if CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_PERIPHERAL)
|
||||||
|
|
||||||
class NimBLEDescriptor;
|
|
||||||
class NimBLEDescriptorCallbacks;
|
|
||||||
|
|
||||||
# include "NimBLELocalValueAttribute.h"
|
# include "NimBLELocalValueAttribute.h"
|
||||||
# include "NimBLECharacteristic.h"
|
|
||||||
# include "NimBLEUUID.h"
|
|
||||||
# include "NimBLEAttValue.h"
|
|
||||||
# include "NimBLEConnInfo.h"
|
|
||||||
|
|
||||||
# include <string>
|
# include <string>
|
||||||
|
|
||||||
|
class NimBLECharacteristic;
|
||||||
|
class NimBLEDescriptorCallbacks;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief A model of a BLE descriptor.
|
* @brief A model of a BLE descriptor.
|
||||||
*/
|
*/
|
||||||
@@ -77,5 +72,5 @@ class NimBLEDescriptorCallbacks {
|
|||||||
|
|
||||||
# include "NimBLE2904.h"
|
# include "NimBLE2904.h"
|
||||||
|
|
||||||
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL */
|
#endif // CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_PERIPHERAL)
|
||||||
#endif /* NIMBLE_CPP_DESCRIPTOR_H_ */
|
#endif // NIMBLE_CPP_DESCRIPTOR_H_
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
|
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
|
||||||
* esp-nimble-cpp, NimBLE-Arduino contributors.
|
* esp-nimble-cpp, NimBLE-Arduino contributors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@@ -15,11 +15,8 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "nimconfig.h"
|
#include "NimBLEDevice.h"
|
||||||
#if defined(CONFIG_BT_ENABLED)
|
#if CONFIG_BT_NIMBLE_ENABLED
|
||||||
|
|
||||||
# include "NimBLEDevice.h"
|
|
||||||
# include "NimBLEUtils.h"
|
|
||||||
|
|
||||||
# ifdef ESP_PLATFORM
|
# ifdef ESP_PLATFORM
|
||||||
# include "esp_err.h"
|
# include "esp_err.h"
|
||||||
@@ -59,14 +56,6 @@
|
|||||||
# include "esp32-hal-bt.h"
|
# include "esp32-hal-bt.h"
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
# if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
|
|
||||||
# include "NimBLEClient.h"
|
|
||||||
# endif
|
|
||||||
|
|
||||||
# if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
|
|
||||||
# include "NimBLEServer.h"
|
|
||||||
# endif
|
|
||||||
|
|
||||||
# include "NimBLELog.h"
|
# include "NimBLELog.h"
|
||||||
|
|
||||||
static const char* LOG_TAG = "NimBLEDevice";
|
static const char* LOG_TAG = "NimBLEDevice";
|
||||||
@@ -76,24 +65,30 @@ extern "C" void ble_store_config_init(void);
|
|||||||
/**
|
/**
|
||||||
* Singletons for the NimBLEDevice.
|
* Singletons for the NimBLEDevice.
|
||||||
*/
|
*/
|
||||||
# if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
|
NimBLEDeviceCallbacks NimBLEDevice::defaultDeviceCallbacks{};
|
||||||
|
NimBLEDeviceCallbacks* NimBLEDevice::m_pDeviceCallbacks = &defaultDeviceCallbacks;
|
||||||
|
|
||||||
|
# if MYNEWT_VAL(BLE_ROLE_OBSERVER)
|
||||||
NimBLEScan* NimBLEDevice::m_pScan = nullptr;
|
NimBLEScan* NimBLEDevice::m_pScan = nullptr;
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
# if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
|
# if MYNEWT_VAL(BLE_ROLE_PERIPHERAL)
|
||||||
NimBLEServer* NimBLEDevice::m_pServer = nullptr;
|
NimBLEServer* NimBLEDevice::m_pServer = nullptr;
|
||||||
|
# if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM)
|
||||||
|
NimBLEL2CAPServer* NimBLEDevice::m_pL2CAPServer = nullptr;
|
||||||
|
# endif
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
# if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
|
# if MYNEWT_VAL(BLE_ROLE_BROADCASTER)
|
||||||
# if CONFIG_BT_NIMBLE_EXT_ADV
|
# if MYNEWT_VAL(BLE_EXT_ADV)
|
||||||
NimBLEExtAdvertising* NimBLEDevice::m_bleAdvertising = nullptr;
|
NimBLEExtAdvertising* NimBLEDevice::m_bleAdvertising = nullptr;
|
||||||
# else
|
# else
|
||||||
NimBLEAdvertising* NimBLEDevice::m_bleAdvertising = nullptr;
|
NimBLEAdvertising* NimBLEDevice::m_bleAdvertising = nullptr;
|
||||||
# endif
|
# endif
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
# if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
|
# if MYNEWT_VAL(BLE_ROLE_CENTRAL)
|
||||||
std::array<NimBLEClient*, NIMBLE_MAX_CONNECTIONS> NimBLEDevice::m_pClients{nullptr};
|
std::array<NimBLEClient*, MYNEWT_VAL(BLE_MAX_CONNECTIONS)> NimBLEDevice::m_pClients{};
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
bool NimBLEDevice::m_initialized{false};
|
bool NimBLEDevice::m_initialized{false};
|
||||||
@@ -103,10 +98,15 @@ ble_gap_event_listener NimBLEDevice::m_listener{};
|
|||||||
std::vector<NimBLEAddress> NimBLEDevice::m_whiteList{};
|
std::vector<NimBLEAddress> NimBLEDevice::m_whiteList{};
|
||||||
uint8_t NimBLEDevice::m_ownAddrType{BLE_OWN_ADDR_PUBLIC};
|
uint8_t NimBLEDevice::m_ownAddrType{BLE_OWN_ADDR_PUBLIC};
|
||||||
|
|
||||||
# ifdef ESP_PLATFORM
|
# if NIMBLE_CPP_SCAN_DUPL_ENABLED
|
||||||
# ifdef CONFIG_BTDM_BLE_SCAN_DUPL
|
uint16_t NimBLEDevice::m_scanDuplicateSize{100};
|
||||||
uint16_t NimBLEDevice::m_scanDuplicateSize{CONFIG_BTDM_SCAN_DUPL_CACHE_SIZE};
|
uint8_t NimBLEDevice::m_scanFilterMode{0};
|
||||||
uint8_t NimBLEDevice::m_scanFilterMode{CONFIG_BTDM_SCAN_DUPL_TYPE};
|
uint16_t NimBLEDevice::m_scanDuplicateResetTime{0};
|
||||||
|
# if SOC_ESP_NIMBLE_CONTROLLER
|
||||||
|
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
|
# endif
|
||||||
|
|
||||||
@@ -114,7 +114,7 @@ uint8_t NimBLEDevice::m_scanFilterMode{CONFIG_BTDM_SCAN_DUPL_TYPE};
|
|||||||
/* SERVER FUNCTIONS */
|
/* SERVER FUNCTIONS */
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
|
|
||||||
# if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
|
# if MYNEWT_VAL(BLE_ROLE_PERIPHERAL)
|
||||||
/**
|
/**
|
||||||
* @brief Create an instance of a server.
|
* @brief Create an instance of a server.
|
||||||
* @return A pointer to the instance of the server.
|
* @return A pointer to the instance of the server.
|
||||||
@@ -122,9 +122,6 @@ uint8_t NimBLEDevice::m_scanFilterMode{CONFIG_BTDM_SCAN_DUPL_TYPE};
|
|||||||
NimBLEServer* NimBLEDevice::createServer() {
|
NimBLEServer* NimBLEDevice::createServer() {
|
||||||
if (NimBLEDevice::m_pServer == nullptr) {
|
if (NimBLEDevice::m_pServer == nullptr) {
|
||||||
NimBLEDevice::m_pServer = new NimBLEServer();
|
NimBLEDevice::m_pServer = new NimBLEServer();
|
||||||
ble_gatts_reset();
|
|
||||||
ble_svc_gap_init();
|
|
||||||
ble_svc_gatt_init();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return m_pServer;
|
return m_pServer;
|
||||||
@@ -137,14 +134,35 @@ NimBLEServer* NimBLEDevice::createServer() {
|
|||||||
NimBLEServer* NimBLEDevice::getServer() {
|
NimBLEServer* NimBLEDevice::getServer() {
|
||||||
return m_pServer;
|
return m_pServer;
|
||||||
} // getServer
|
} // getServer
|
||||||
# endif // #if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
|
|
||||||
|
# if MYNEWT_VAL(BLE_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
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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 MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM)
|
||||||
|
# endif // #if MYNEWT_VAL(BLE_ROLE_PERIPHERAL)
|
||||||
|
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
/* ADVERTISING FUNCTIONS */
|
/* ADVERTISING FUNCTIONS */
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
|
|
||||||
# if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
|
# if MYNEWT_VAL(BLE_ROLE_BROADCASTER)
|
||||||
# if CONFIG_BT_NIMBLE_EXT_ADV
|
# if MYNEWT_VAL(BLE_EXT_ADV)
|
||||||
/**
|
/**
|
||||||
* @brief Get the instance of the extended advertising object.
|
* @brief Get the instance of the extended advertising object.
|
||||||
* @return A pointer to the extended advertising object.
|
* @return A pointer to the extended advertising object.
|
||||||
@@ -178,7 +196,7 @@ bool NimBLEDevice::stopAdvertising(uint8_t instId) {
|
|||||||
} // stopAdvertising
|
} // stopAdvertising
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
# if !CONFIG_BT_NIMBLE_EXT_ADV || defined(_DOXYGEN_)
|
# if !MYNEWT_VAL(BLE_EXT_ADV) || defined(_DOXYGEN_)
|
||||||
/**
|
/**
|
||||||
* @brief Get the instance of the advertising object.
|
* @brief Get the instance of the advertising object.
|
||||||
* @return A pointer to the advertising object.
|
* @return A pointer to the advertising object.
|
||||||
@@ -207,7 +225,7 @@ bool NimBLEDevice::startAdvertising(uint32_t duration) {
|
|||||||
bool NimBLEDevice::stopAdvertising() {
|
bool NimBLEDevice::stopAdvertising() {
|
||||||
return getAdvertising()->stop();
|
return getAdvertising()->stop();
|
||||||
} // stopAdvertising
|
} // stopAdvertising
|
||||||
# endif // #if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
|
# endif // #if MYNEWT_VAL(BLE_ROLE_BROADCASTER)
|
||||||
|
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
/* SCAN FUNCTIONS */
|
/* SCAN FUNCTIONS */
|
||||||
@@ -218,7 +236,7 @@ bool NimBLEDevice::stopAdvertising() {
|
|||||||
* @return The scanning object reference. This is a singleton object. The caller should not
|
* @return The scanning object reference. This is a singleton object. The caller should not
|
||||||
* try and release/delete it.
|
* try and release/delete it.
|
||||||
*/
|
*/
|
||||||
# if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
|
# if MYNEWT_VAL(BLE_ROLE_OBSERVER)
|
||||||
NimBLEScan* NimBLEDevice::getScan() {
|
NimBLEScan* NimBLEDevice::getScan() {
|
||||||
if (m_pScan == nullptr) {
|
if (m_pScan == nullptr) {
|
||||||
m_pScan = new NimBLEScan();
|
m_pScan = new NimBLEScan();
|
||||||
@@ -228,7 +246,7 @@ NimBLEScan* NimBLEDevice::getScan() {
|
|||||||
} // getScan
|
} // getScan
|
||||||
|
|
||||||
# ifdef ESP_PLATFORM
|
# ifdef ESP_PLATFORM
|
||||||
# ifdef CONFIG_BTDM_BLE_SCAN_DUPL
|
# if CONFIG_BTDM_BLE_SCAN_DUPL || CONFIG_BT_LE_SCAN_DUPL
|
||||||
/**
|
/**
|
||||||
* @brief Set the duplicate filter cache size for filtering scanned devices.
|
* @brief Set the duplicate filter cache size for filtering scanned devices.
|
||||||
* @param [in] size 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
|
||||||
@@ -275,15 +293,34 @@ void NimBLEDevice::setScanFilterMode(uint8_t mode) {
|
|||||||
|
|
||||||
m_scanFilterMode = mode;
|
m_scanFilterMode = mode;
|
||||||
}
|
}
|
||||||
# endif // CONFIG_BTDM_BLE_SCAN_DUPL
|
|
||||||
|
/**
|
||||||
|
* @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 // ESP_PLATFORM
|
||||||
# endif // #if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
|
# endif // MYNEWT_VAL(BLE_ROLE_OBSERVER)
|
||||||
|
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
/* CLIENT FUNCTIONS */
|
/* CLIENT FUNCTIONS */
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
|
|
||||||
# if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
|
# if MYNEWT_VAL(BLE_ROLE_CENTRAL)
|
||||||
/**
|
/**
|
||||||
* @brief Creates a new client object, each client can connect to 1 peripheral device.
|
* @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.
|
* @return A pointer to the new client object, or nullptr on error.
|
||||||
@@ -306,7 +343,7 @@ NimBLEClient* NimBLEDevice::createClient(const NimBLEAddress& peerAddress) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NIMBLE_LOGE(LOG_TAG, "Unable to create client; already at max: %d", NIMBLE_MAX_CONNECTIONS);
|
NIMBLE_LOGE(LOG_TAG, "Unable to create client; already at max: %d", MYNEWT_VAL(BLE_MAX_CONNECTIONS));
|
||||||
return nullptr;
|
return nullptr;
|
||||||
} // createClient
|
} // createClient
|
||||||
|
|
||||||
@@ -418,7 +455,7 @@ std::vector<NimBLEClient*> NimBLEDevice::getConnectedClients() {
|
|||||||
return clients;
|
return clients;
|
||||||
} // getConnectedClients
|
} // getConnectedClients
|
||||||
|
|
||||||
# endif // #if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
|
# endif // MYNEWT_VAL(BLE_ROLE_CENTRAL)
|
||||||
|
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
/* TRANSMIT POWER */
|
/* TRANSMIT POWER */
|
||||||
@@ -464,7 +501,7 @@ bool NimBLEDevice::setPowerLevel(esp_power_level_t powerLevel, esp_ble_power_typ
|
|||||||
* @param [in] dbm The power level to set in dBm.
|
* @param [in] dbm The power level to set in dBm.
|
||||||
* @return True if the power level was set successfully.
|
* @return True if the power level was set successfully.
|
||||||
*/
|
*/
|
||||||
bool NimBLEDevice::setPower(int8_t dbm) {
|
bool NimBLEDevice::setPower(int8_t dbm, NimBLETxPowerType type) {
|
||||||
# ifdef ESP_PLATFORM
|
# ifdef ESP_PLATFORM
|
||||||
# ifdef CONFIG_IDF_TARGET_ESP32P4
|
# ifdef CONFIG_IDF_TARGET_ESP32P4
|
||||||
return false; // CONFIG_IDF_TARGET_ESP32P4 does not support esp_ble_tx_power_set
|
return false; // CONFIG_IDF_TARGET_ESP32P4 does not support esp_ble_tx_power_set
|
||||||
@@ -472,19 +509,33 @@ bool NimBLEDevice::setPower(int8_t dbm) {
|
|||||||
if (dbm % 3 == 2) {
|
if (dbm % 3 == 2) {
|
||||||
dbm++; // round up to the next multiple of 3 to be able to target 20dbm
|
dbm++; // round up to the next multiple of 3 to be able to target 20dbm
|
||||||
}
|
}
|
||||||
return setPowerLevel(static_cast<esp_power_level_t>(dbm / 3 + ESP_PWR_LVL_N0));
|
|
||||||
|
bool success = false;
|
||||||
|
esp_power_level_t espPwr = static_cast<esp_power_level_t>(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
|
# endif
|
||||||
# else
|
# else
|
||||||
|
(void)type; // unused
|
||||||
NIMBLE_LOGD(LOG_TAG, ">> setPower: %d", dbm);
|
NIMBLE_LOGD(LOG_TAG, ">> setPower: %d", dbm);
|
||||||
ble_hci_vs_set_tx_pwr_cp cmd{dbm};
|
int rc = ble_phy_tx_power_set(dbm);
|
||||||
ble_hci_vs_set_tx_pwr_rp rsp{0};
|
|
||||||
int rc = ble_hs_hci_send_vs_cmd(BLE_HCI_OCF_VS_SET_TX_PWR, &cmd, sizeof(cmd), &rsp, sizeof(rsp));
|
|
||||||
if (rc) {
|
if (rc) {
|
||||||
NIMBLE_LOGE(LOG_TAG, "failed to set TX power, rc: %04x\n", rc);
|
NIMBLE_LOGE(LOG_TAG, "failed to set TX power, rc: %04x\n", rc);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
NIMBLE_LOGD(LOG_TAG, "TX power set to %d dBm\n", rsp.tx_power);
|
NIMBLE_LOGD(LOG_TAG, "TX power set to %d dBm\n", dbm);
|
||||||
return true;
|
return true;
|
||||||
# endif
|
# endif
|
||||||
} // setPower
|
} // setPower
|
||||||
@@ -493,12 +544,16 @@ bool NimBLEDevice::setPower(int8_t dbm) {
|
|||||||
* @brief Get the transmission power.
|
* @brief Get the transmission power.
|
||||||
* @return The power level currently used in dbm or 0xFF on error.
|
* @return The power level currently used in dbm or 0xFF on error.
|
||||||
*/
|
*/
|
||||||
int NimBLEDevice::getPower() {
|
int NimBLEDevice::getPower(NimBLETxPowerType type) {
|
||||||
# ifdef ESP_PLATFORM
|
# ifdef ESP_PLATFORM
|
||||||
# ifdef CONFIG_IDF_TARGET_ESP32P4
|
# ifdef CONFIG_IDF_TARGET_ESP32P4
|
||||||
return 0xFF; // CONFIG_IDF_TARGET_ESP32P4 does not support esp_ble_tx_power_get
|
return 0xFF; // CONFIG_IDF_TARGET_ESP32P4 does not support esp_ble_tx_power_get
|
||||||
# else
|
# else
|
||||||
int pwr = getPowerLevel();
|
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) {
|
if (pwr < 0) {
|
||||||
NIMBLE_LOGE(LOG_TAG, "esp_ble_tx_power_get failed rc=%d", pwr);
|
NIMBLE_LOGE(LOG_TAG, "esp_ble_tx_power_get failed rc=%d", pwr);
|
||||||
return 0xFF;
|
return 0xFF;
|
||||||
@@ -515,7 +570,8 @@ int NimBLEDevice::getPower() {
|
|||||||
return 0;
|
return 0;
|
||||||
# endif
|
# endif
|
||||||
# else
|
# else
|
||||||
return ble_phy_txpwr_get();
|
(void)type; // unused
|
||||||
|
return ble_phy_tx_power_get();
|
||||||
# endif
|
# endif
|
||||||
} // getPower
|
} // getPower
|
||||||
|
|
||||||
@@ -550,11 +606,12 @@ uint16_t NimBLEDevice::getMTU() {
|
|||||||
/* BOND MANAGEMENT */
|
/* BOND MANAGEMENT */
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
|
|
||||||
# if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL) || defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
|
# if MYNEWT_VAL(BLE_ROLE_CENTRAL) || MYNEWT_VAL(BLE_ROLE_PERIPHERAL)
|
||||||
/**
|
/**
|
||||||
* @brief Gets the number of bonded peers stored
|
* @brief Gets the number of bonded peers stored
|
||||||
*/
|
*/
|
||||||
int NimBLEDevice::getNumBonds() {
|
int NimBLEDevice::getNumBonds() {
|
||||||
|
# if MYNEWT_VAL(BLE_STORE_MAX_BONDS)
|
||||||
ble_addr_t peer_id_addrs[MYNEWT_VAL(BLE_STORE_MAX_BONDS)];
|
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));
|
rc = ble_store_util_bonded_peers(&peer_id_addrs[0], &num_peers, MYNEWT_VAL(BLE_STORE_MAX_BONDS));
|
||||||
@@ -563,6 +620,9 @@ int NimBLEDevice::getNumBonds() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return num_peers;
|
return num_peers;
|
||||||
|
# else
|
||||||
|
return 0;
|
||||||
|
# endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -570,10 +630,13 @@ int NimBLEDevice::getNumBonds() {
|
|||||||
* @returns True on success.
|
* @returns True on success.
|
||||||
*/
|
*/
|
||||||
bool NimBLEDevice::deleteAllBonds() {
|
bool NimBLEDevice::deleteAllBonds() {
|
||||||
int rc = ble_store_clear();
|
int numBonds = NimBLEDevice::getNumBonds();
|
||||||
if (rc != 0) {
|
for (int i = numBonds - 1; i >= 0; i--) {
|
||||||
NIMBLE_LOGE(LOG_TAG, "Failed to delete all bonds; rc=%d", rc);
|
auto addr = NimBLEDevice::getBondedAddress(i);
|
||||||
return false;
|
if (!NimBLEDevice::deleteBond(addr)) {
|
||||||
|
NIMBLE_LOGE(LOG_TAG, "Failed to delete bond for address: %s", addr.toString().c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -593,6 +656,7 @@ bool NimBLEDevice::deleteBond(const NimBLEAddress& address) {
|
|||||||
* @returns True if bonded.
|
* @returns True if bonded.
|
||||||
*/
|
*/
|
||||||
bool NimBLEDevice::isBonded(const NimBLEAddress& address) {
|
bool NimBLEDevice::isBonded(const NimBLEAddress& address) {
|
||||||
|
# if MYNEWT_VAL(BLE_STORE_MAX_BONDS)
|
||||||
ble_addr_t peer_id_addrs[MYNEWT_VAL(BLE_STORE_MAX_BONDS)];
|
ble_addr_t peer_id_addrs[MYNEWT_VAL(BLE_STORE_MAX_BONDS)];
|
||||||
int num_peers, rc;
|
int num_peers, rc;
|
||||||
|
|
||||||
@@ -607,7 +671,8 @@ bool NimBLEDevice::isBonded(const NimBLEAddress& address) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
# endif
|
||||||
|
(void)address; // unused
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -617,14 +682,19 @@ bool NimBLEDevice::isBonded(const NimBLEAddress& address) {
|
|||||||
* @returns NimBLEAddress of the found bonded peer or null address if not found.
|
* @returns NimBLEAddress of the found bonded peer or null address if not found.
|
||||||
*/
|
*/
|
||||||
NimBLEAddress NimBLEDevice::getBondedAddress(int index) {
|
NimBLEAddress NimBLEDevice::getBondedAddress(int index) {
|
||||||
|
# if MYNEWT_VAL(BLE_STORE_MAX_BONDS)
|
||||||
ble_addr_t peer_id_addrs[MYNEWT_VAL(BLE_STORE_MAX_BONDS)];
|
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));
|
rc = ble_store_util_bonded_peers(&peer_id_addrs[0], &num_peers, MYNEWT_VAL(BLE_STORE_MAX_BONDS));
|
||||||
if (rc != 0 || index > num_peers || index < 0) {
|
if (rc != 0 || index >= num_peers || index < 0) {
|
||||||
return NimBLEAddress{};
|
return NimBLEAddress{};
|
||||||
}
|
}
|
||||||
|
|
||||||
return NimBLEAddress(peer_id_addrs[index]);
|
return NimBLEAddress(peer_id_addrs[index]);
|
||||||
|
# else
|
||||||
|
(void)index; // unused
|
||||||
|
return NimBLEAddress{};
|
||||||
|
# endif
|
||||||
}
|
}
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
@@ -703,7 +773,7 @@ size_t NimBLEDevice::getWhiteListCount() {
|
|||||||
* @returns The NimBLEAddress at the whitelist index or null address if not found.
|
* @returns The NimBLEAddress at the whitelist index or null address if not found.
|
||||||
*/
|
*/
|
||||||
NimBLEAddress NimBLEDevice::getWhiteListAddress(size_t index) {
|
NimBLEAddress NimBLEDevice::getWhiteListAddress(size_t index) {
|
||||||
if (index > m_whiteList.size()) {
|
if (index >= m_whiteList.size()) {
|
||||||
NIMBLE_LOGE(LOG_TAG, "Invalid index; %u", index);
|
NIMBLE_LOGE(LOG_TAG, "Invalid index; %u", index);
|
||||||
return NimBLEAddress{};
|
return NimBLEAddress{};
|
||||||
}
|
}
|
||||||
@@ -715,7 +785,6 @@ NimBLEAddress NimBLEDevice::getWhiteListAddress(size_t index) {
|
|||||||
/* STACK FUNCTIONS */
|
/* STACK FUNCTIONS */
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
|
|
||||||
# if CONFIG_BT_NIMBLE_EXT_ADV || defined(_DOXYGEN_)
|
|
||||||
/**
|
/**
|
||||||
* @brief Set the preferred default phy to use for connections.
|
* @brief Set the preferred default phy to use for connections.
|
||||||
* @param [in] txPhyMask TX PHY. Can be mask of following:
|
* @param [in] txPhyMask TX PHY. Can be mask of following:
|
||||||
@@ -738,7 +807,6 @@ bool NimBLEDevice::setDefaultPhy(uint8_t txPhyMask, uint8_t rxPhyMask) {
|
|||||||
|
|
||||||
return rc == 0;
|
return rc == 0;
|
||||||
}
|
}
|
||||||
# endif
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Host reset, we pass the message so we don't make calls until re-synced.
|
* @brief Host reset, we pass the message so we don't make calls until re-synced.
|
||||||
@@ -789,13 +857,13 @@ void NimBLEDevice::onSync(void) {
|
|||||||
m_synced = true;
|
m_synced = true;
|
||||||
|
|
||||||
if (m_initialized) {
|
if (m_initialized) {
|
||||||
# if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
|
# if MYNEWT_VAL(BLE_ROLE_OBSERVER)
|
||||||
if (m_pScan != nullptr) {
|
if (m_pScan != nullptr) {
|
||||||
m_pScan->onHostSync();
|
m_pScan->onHostSync();
|
||||||
}
|
}
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
# if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
|
# if MYNEWT_VAL(BLE_ROLE_BROADCASTER)
|
||||||
if (m_bleAdvertising != nullptr) {
|
if (m_bleAdvertising != nullptr) {
|
||||||
m_bleAdvertising->onHostSync();
|
m_bleAdvertising->onHostSync();
|
||||||
}
|
}
|
||||||
@@ -807,7 +875,7 @@ void NimBLEDevice::onSync(void) {
|
|||||||
* @brief The main host task.
|
* @brief The main host task.
|
||||||
*/
|
*/
|
||||||
void NimBLEDevice::host_task(void* param) {
|
void NimBLEDevice::host_task(void* param) {
|
||||||
NIMBLE_LOGI(LOG_TAG, "BLE Host Task Started");
|
NIMBLE_LOGI(LOG_TAG, "NimBLE Started!");
|
||||||
nimble_port_run(); // This function will return only when nimble_port_stop() is executed
|
nimble_port_run(); // This function will return only when nimble_port_stop() is executed
|
||||||
nimble_port_freertos_deinit();
|
nimble_port_freertos_deinit();
|
||||||
} // host_task
|
} // host_task
|
||||||
@@ -818,9 +886,10 @@ void NimBLEDevice::host_task(void* param) {
|
|||||||
*/
|
*/
|
||||||
bool NimBLEDevice::init(const std::string& deviceName) {
|
bool NimBLEDevice::init(const std::string& deviceName) {
|
||||||
if (!m_initialized) {
|
if (!m_initialized) {
|
||||||
|
NIMBLE_LOGD(LOG_TAG, "Starting %s", getVersion());
|
||||||
# ifdef ESP_PLATFORM
|
# 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.
|
// make sure the linker includes esp32-hal-bt.c so Arduino init doesn't release BLE memory.
|
||||||
btStarted();
|
btStarted();
|
||||||
# endif
|
# endif
|
||||||
@@ -842,52 +911,91 @@ bool NimBLEDevice::init(const std::string& deviceName) {
|
|||||||
esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT);
|
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();
|
esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
|
||||||
# if defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S3)
|
# if defined(CONFIG_IDF_TARGET_ESP32)
|
||||||
bt_cfg.bluetooth_mode = ESP_BT_MODE_BLE;
|
|
||||||
# else
|
|
||||||
bt_cfg.mode = ESP_BT_MODE_BLE;
|
bt_cfg.mode = ESP_BT_MODE_BLE;
|
||||||
bt_cfg.ble_max_conn = CONFIG_BT_NIMBLE_MAX_CONNECTIONS;
|
bt_cfg.ble_max_conn = MYNEWT_VAL(BLE_MAX_CONNECTIONS);
|
||||||
|
# elif defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||||
|
bt_cfg.ble_max_act =
|
||||||
|
MYNEWT_VAL(BLE_MAX_CONNECTIONS) + MYNEWT_VAL(BLE_ROLE_BROADCASTER) + MYNEWT_VAL(BLE_ROLE_OBSERVER);
|
||||||
|
# else
|
||||||
|
bt_cfg.nimble_max_connections = MYNEWT_VAL(BLE_MAX_CONNECTIONS);
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
# ifdef CONFIG_BTDM_BLE_SCAN_DUPL
|
# if NIMBLE_CPP_SCAN_DUPL_ENABLED
|
||||||
|
# if !SOC_ESP_NIMBLE_CONTROLLER
|
||||||
bt_cfg.normal_adv_size = m_scanDuplicateSize;
|
bt_cfg.normal_adv_size = m_scanDuplicateSize;
|
||||||
bt_cfg.scan_duplicate_type = m_scanFilterMode;
|
bt_cfg.scan_duplicate_type = m_scanFilterMode;
|
||||||
# endif
|
bt_cfg.scan_duplicate_mode = 0; // Ensure normal filter mode, could be set to mesh in default config
|
||||||
|
# if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||||
|
bt_cfg.dup_list_refresh_period = m_scanDuplicateResetTime;
|
||||||
|
# endif
|
||||||
|
# else // SOC_ESP_NIMBLE_CONTROLLER
|
||||||
|
bt_cfg.ble_ll_rsp_dup_list_count = m_scanDuplicateSize;
|
||||||
|
bt_cfg.ble_ll_adv_dup_list_count = m_scanDuplicateSize;
|
||||||
|
# endif // SOC_ESP_NIMBLE_CONTROLLER
|
||||||
err = esp_bt_controller_init(&bt_cfg);
|
err = esp_bt_controller_init(&bt_cfg);
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
NIMBLE_LOGE(LOG_TAG, "esp_bt_controller_init() failed; err=%d", err);
|
NIMBLE_LOGE(LOG_TAG, "esp_bt_controller_init() failed; err=%d", err);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# if SOC_ESP_NIMBLE_CONTROLLER
|
||||||
|
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 // SOC_ESP_NIMBLE_CONTROLLER
|
||||||
|
# endif // NIMBLE_CPP_SCAN_DUPL_ENABLED
|
||||||
|
|
||||||
err = esp_bt_controller_enable(ESP_BT_MODE_BLE);
|
err = esp_bt_controller_enable(ESP_BT_MODE_BLE);
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
NIMBLE_LOGE(LOG_TAG, "esp_bt_controller_enable() failed; err=%d", err);
|
NIMBLE_LOGE(LOG_TAG, "esp_bt_controller_enable() failed; err=%d", err);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# if CONFIG_BT_NIMBLE_LEGACY_VHCI_ENABLE
|
||||||
err = esp_nimble_hci_init();
|
err = esp_nimble_hci_init();
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
NIMBLE_LOGE(LOG_TAG, "esp_nimble_hci_init() failed; err=%d", err);
|
NIMBLE_LOGE(LOG_TAG, "esp_nimble_hci_init() failed; err=%d", err);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
# endif
|
||||||
# endif
|
# endif
|
||||||
# endif
|
# endif
|
||||||
nimble_port_init();
|
nimble_port_init();
|
||||||
|
|
||||||
// Setup callbacks for host events
|
// Setup callbacks for host events
|
||||||
ble_hs_cfg.reset_cb = NimBLEDevice::onReset;
|
ble_hs_cfg.reset_cb = NimBLEDevice::onReset;
|
||||||
ble_hs_cfg.sync_cb = NimBLEDevice::onSync;
|
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
|
// Set initial security capabilities
|
||||||
ble_hs_cfg.sm_io_cap = BLE_HS_IO_NO_INPUT_OUTPUT;
|
ble_hs_cfg.sm_io_cap = BLE_HS_IO_NO_INPUT_OUTPUT;
|
||||||
ble_hs_cfg.sm_bonding = 0;
|
ble_hs_cfg.sm_bonding = 0;
|
||||||
ble_hs_cfg.sm_mitm = 0;
|
ble_hs_cfg.sm_mitm = 0;
|
||||||
ble_hs_cfg.sm_sc = 1;
|
ble_hs_cfg.sm_sc = 0;
|
||||||
ble_hs_cfg.sm_our_key_dist = BLE_SM_PAIR_KEY_DIST_ENC | BLE_SM_PAIR_KEY_DIST_ID;
|
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 | BLE_SM_PAIR_KEY_DIST_ID;
|
ble_hs_cfg.sm_their_key_dist = BLE_SM_PAIR_KEY_DIST_ENC;
|
||||||
ble_hs_cfg.store_status_cb = ble_store_util_status_rr; /*TODO: Implement handler for this*/
|
# 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);
|
setDeviceName(deviceName);
|
||||||
ble_store_config_init();
|
ble_store_config_init();
|
||||||
@@ -900,6 +1008,7 @@ bool NimBLEDevice::init(const std::string& deviceName) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
m_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.
|
||||||
|
NIMBLE_LOGD(LOG_TAG, "Initialized");
|
||||||
return true;
|
return true;
|
||||||
} // init
|
} // init
|
||||||
|
|
||||||
@@ -930,28 +1039,34 @@ bool NimBLEDevice::deinit(bool clearAll) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (clearAll) {
|
if (clearAll) {
|
||||||
# if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
|
# if MYNEWT_VAL(BLE_ROLE_PERIPHERAL)
|
||||||
if (NimBLEDevice::m_pServer != nullptr) {
|
if (NimBLEDevice::m_pServer != nullptr) {
|
||||||
delete NimBLEDevice::m_pServer;
|
delete NimBLEDevice::m_pServer;
|
||||||
NimBLEDevice::m_pServer = nullptr;
|
NimBLEDevice::m_pServer = nullptr;
|
||||||
}
|
}
|
||||||
|
# if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM)
|
||||||
|
if (NimBLEDevice::m_pL2CAPServer != nullptr) {
|
||||||
|
delete NimBLEDevice::m_pL2CAPServer;
|
||||||
|
NimBLEDevice::m_pL2CAPServer = nullptr;
|
||||||
|
}
|
||||||
|
# endif
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
# if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
|
# if MYNEWT_VAL(BLE_ROLE_BROADCASTER)
|
||||||
if (NimBLEDevice::m_bleAdvertising != nullptr) {
|
if (NimBLEDevice::m_bleAdvertising != nullptr) {
|
||||||
delete NimBLEDevice::m_bleAdvertising;
|
delete NimBLEDevice::m_bleAdvertising;
|
||||||
NimBLEDevice::m_bleAdvertising = nullptr;
|
NimBLEDevice::m_bleAdvertising = nullptr;
|
||||||
}
|
}
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
# if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
|
# if MYNEWT_VAL(BLE_ROLE_OBSERVER)
|
||||||
if (NimBLEDevice::m_pScan != nullptr) {
|
if (NimBLEDevice::m_pScan != nullptr) {
|
||||||
delete NimBLEDevice::m_pScan;
|
delete NimBLEDevice::m_pScan;
|
||||||
NimBLEDevice::m_pScan = nullptr;
|
NimBLEDevice::m_pScan = nullptr;
|
||||||
}
|
}
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
# if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
|
# if MYNEWT_VAL(BLE_ROLE_CENTRAL)
|
||||||
for (auto clt : m_pClients) {
|
for (auto clt : m_pClients) {
|
||||||
deleteClient(clt);
|
deleteClient(clt);
|
||||||
}
|
}
|
||||||
@@ -1008,18 +1123,16 @@ bool NimBLEDevice::setOwnAddrType(uint8_t type) {
|
|||||||
|
|
||||||
m_ownAddrType = type;
|
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) {
|
if (type == BLE_OWN_ADDR_RPA_PUBLIC_DEFAULT || type == BLE_OWN_ADDR_RPA_RANDOM_DEFAULT) {
|
||||||
# ifdef CONFIG_IDF_TARGET_ESP32
|
|
||||||
// esp32 controller does not support RPA so we must use the random static for calls to the stack
|
// 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.
|
// the host will take care of the random private address generation/setting.
|
||||||
m_ownAddrType = BLE_OWN_ADDR_RANDOM;
|
m_ownAddrType = BLE_OWN_ADDR_RANDOM;
|
||||||
rc = ble_hs_pvcy_rpa_config(NIMBLE_HOST_ENABLE_RPA);
|
rc = ble_hs_pvcy_rpa_config(NIMBLE_HOST_ENABLE_RPA);
|
||||||
# endif
|
|
||||||
} else {
|
} else {
|
||||||
# ifdef CONFIG_IDF_TARGET_ESP32
|
|
||||||
rc = ble_hs_pvcy_rpa_config(NIMBLE_HOST_DISABLE_PRIVACY);
|
rc = ble_hs_pvcy_rpa_config(NIMBLE_HOST_DISABLE_PRIVACY);
|
||||||
# endif
|
|
||||||
}
|
}
|
||||||
|
# endif
|
||||||
|
|
||||||
return rc == 0;
|
return rc == 0;
|
||||||
} // setOwnAddrType
|
} // setOwnAddrType
|
||||||
@@ -1154,6 +1267,7 @@ bool NimBLEDevice::startSecurity(uint16_t connHandle, int* rcPtr) {
|
|||||||
return rc == 0 || rc == BLE_HS_EALREADY;
|
return rc == 0 || rc == BLE_HS_EALREADY;
|
||||||
} // startSecurity
|
} // startSecurity
|
||||||
|
|
||||||
|
# if MYNEWT_VAL(BLE_ROLE_CENTRAL) || MYNEWT_VAL(BLE_ROLE_PERIPHERAL)
|
||||||
/**
|
/**
|
||||||
* @brief Inject the provided passkey into the Security Manager.
|
* @brief Inject the provided passkey into the Security Manager.
|
||||||
* @param [in] peerInfo Connection information for the peer.
|
* @param [in] peerInfo Connection information for the peer.
|
||||||
@@ -1161,10 +1275,17 @@ bool NimBLEDevice::startSecurity(uint16_t connHandle, int* rcPtr) {
|
|||||||
* @return true if the passkey was injected successfully.
|
* @return true if the passkey was injected successfully.
|
||||||
*/
|
*/
|
||||||
bool NimBLEDevice::injectPassKey(const NimBLEConnInfo& peerInfo, uint32_t passkey) {
|
bool NimBLEDevice::injectPassKey(const NimBLEConnInfo& peerInfo, uint32_t passkey) {
|
||||||
|
#if MYNEWT_VAL(BLE_SM_LEGACY)
|
||||||
ble_sm_io pkey{.action = BLE_SM_IOACT_INPUT, .passkey = passkey};
|
ble_sm_io pkey{.action = BLE_SM_IOACT_INPUT, .passkey = passkey};
|
||||||
int rc = ble_sm_inject_io(peerInfo.getConnHandle(), &pkey);
|
int rc = ble_sm_inject_io(peerInfo.getConnHandle(), &pkey);
|
||||||
NIMBLE_LOGD(LOG_TAG, "BLE_SM_IOACT_INPUT; ble_sm_inject_io result: %d", rc);
|
NIMBLE_LOGD(LOG_TAG, "BLE_SM_IOACT_INPUT; ble_sm_inject_io result: %d", rc);
|
||||||
return rc == 0;
|
return rc == 0;
|
||||||
|
#else
|
||||||
|
(void)peerInfo;
|
||||||
|
(void)passkey;
|
||||||
|
NIMBLE_LOGE(LOG_TAG, "Passkey entry not supported with current security settings");
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1173,11 +1294,19 @@ bool NimBLEDevice::injectPassKey(const NimBLEConnInfo& peerInfo, uint32_t passke
|
|||||||
* @param [in] accept Whether the user confirmed or declined the comparison.
|
* @param [in] accept Whether the user confirmed or declined the comparison.
|
||||||
*/
|
*/
|
||||||
bool NimBLEDevice::injectConfirmPasskey(const NimBLEConnInfo& peerInfo, bool accept) {
|
bool NimBLEDevice::injectConfirmPasskey(const NimBLEConnInfo& peerInfo, bool accept) {
|
||||||
|
#if MYNEWT_VAL(BLE_SM_SC)
|
||||||
ble_sm_io pkey{.action = BLE_SM_IOACT_NUMCMP, .numcmp_accept = accept};
|
ble_sm_io pkey{.action = BLE_SM_IOACT_NUMCMP, .numcmp_accept = accept};
|
||||||
int rc = ble_sm_inject_io(peerInfo.getConnHandle(), &pkey);
|
int rc = ble_sm_inject_io(peerInfo.getConnHandle(), &pkey);
|
||||||
NIMBLE_LOGD(LOG_TAG, "BLE_SM_IOACT_NUMCMP; ble_sm_inject_io result: %d", rc);
|
NIMBLE_LOGD(LOG_TAG, "BLE_SM_IOACT_NUMCMP; ble_sm_inject_io result: %d", rc);
|
||||||
return rc == 0;
|
return rc == 0;
|
||||||
|
#else
|
||||||
|
(void)peerInfo;
|
||||||
|
(void)accept;
|
||||||
|
NIMBLE_LOGE(LOG_TAG, "Numeric comparison not supported with current security settings");
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
# endif // MYNEWT_VAL(BLE_ROLE_CENTRAL) || MYNEWT_VAL(BLE_ROLE_PERIPHERAL)
|
||||||
|
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
/* UTILITIES */
|
/* UTILITIES */
|
||||||
@@ -1188,22 +1317,24 @@ bool NimBLEDevice::injectConfirmPasskey(const NimBLEConnInfo& peerInfo, bool acc
|
|||||||
* @param [in] deviceName The name to set.
|
* @param [in] deviceName The name to set.
|
||||||
*/
|
*/
|
||||||
bool NimBLEDevice::setDeviceName(const std::string& deviceName) {
|
bool NimBLEDevice::setDeviceName(const std::string& deviceName) {
|
||||||
|
# if !defined(MYNEWT_VAL_BLE_GATTS) || MYNEWT_VAL(BLE_GATTS) > 0
|
||||||
int rc = ble_svc_gap_device_name_set(deviceName.c_str());
|
int rc = ble_svc_gap_device_name_set(deviceName.c_str());
|
||||||
if (rc != 0) {
|
if (rc != 0) {
|
||||||
NIMBLE_LOGE(LOG_TAG, "Device name not set - too long");
|
NIMBLE_LOGE(LOG_TAG, "Device name not set - too long");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
# endif
|
||||||
return true;
|
return true;
|
||||||
} // setDeviceName
|
} // setDeviceName
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Set a custom callback for gap events.
|
* @brief Set a custom callback for gap events.
|
||||||
* @param [in] handler The function to call when gap events occur.
|
* @param [in] handler The function to call when gap events occur.
|
||||||
|
* @param [in] arg Argument to pass to the handler.
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
bool NimBLEDevice::setCustomGapHandler(gap_event_handler handler) {
|
bool NimBLEDevice::setCustomGapHandler(gap_event_handler handler, void* arg) {
|
||||||
int rc = ble_gap_event_listener_register(&m_listener, handler, NULL);
|
int rc = ble_gap_event_listener_register(&m_listener, handler, arg);
|
||||||
if (rc == BLE_HS_EALREADY) {
|
if (rc == BLE_HS_EALREADY) {
|
||||||
NIMBLE_LOGI(LOG_TAG, "Already listening to GAP events.");
|
NIMBLE_LOGI(LOG_TAG, "Already listening to GAP events.");
|
||||||
return true;
|
return true;
|
||||||
@@ -1222,7 +1353,15 @@ std::string NimBLEDevice::toString() {
|
|||||||
return getAddress().toString();
|
return getAddress().toString();
|
||||||
} // toString
|
} // toString
|
||||||
|
|
||||||
# if CONFIG_NIMBLE_CPP_DEBUG_ASSERT_ENABLED || __DOXYGEN__
|
/**
|
||||||
|
* @brief Return the library version as a string.
|
||||||
|
* @return A const char* containing library version information.
|
||||||
|
*/
|
||||||
|
const char* NimBLEDevice::getVersion() {
|
||||||
|
return NIMBLE_CPP_VERSION_STR;
|
||||||
|
} // getVersion
|
||||||
|
|
||||||
|
# if MYNEWT_VAL(NIMBLE_CPP_DEBUG_ASSERT_ENABLED) || __DOXYGEN__
|
||||||
/**
|
/**
|
||||||
* @brief Debug assert - weak function.
|
* @brief Debug assert - weak function.
|
||||||
* @param [in] file The file where the assert occurred.
|
* @param [in] file The file where the assert occurred.
|
||||||
@@ -1233,6 +1372,15 @@ void nimble_cpp_assert(const char* file, unsigned line) {
|
|||||||
ble_npl_time_delay(10);
|
ble_npl_time_delay(10);
|
||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
# endif // CONFIG_NIMBLE_CPP_DEBUG_ASSERT_ENABLED
|
# endif // MYNEWT_VAL(NIMBLE_CPP_DEBUG_ASSERT_ENABLED)
|
||||||
|
|
||||||
#endif // CONFIG_BT_ENABLED
|
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_NIMBLE_ENABLED
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
|
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
|
||||||
* esp-nimble-cpp, NimBLE-Arduino contributors.
|
* esp-nimble-cpp, NimBLE-Arduino contributors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@@ -18,12 +18,15 @@
|
|||||||
#ifndef NIMBLE_CPP_DEVICE_H_
|
#ifndef NIMBLE_CPP_DEVICE_H_
|
||||||
#define NIMBLE_CPP_DEVICE_H_
|
#define NIMBLE_CPP_DEVICE_H_
|
||||||
|
|
||||||
#include "nimconfig.h"
|
#include "NimBLECppVersion.h"
|
||||||
#if defined(CONFIG_BT_ENABLED)
|
#include "syscfg/syscfg.h"
|
||||||
|
#if CONFIG_BT_NIMBLE_ENABLED
|
||||||
# ifdef ESP_PLATFORM
|
# ifdef ESP_PLATFORM
|
||||||
# ifndef CONFIG_IDF_TARGET_ESP32P4
|
# ifndef CONFIG_IDF_TARGET_ESP32P4
|
||||||
# include <esp_bt.h>
|
# include <esp_bt.h>
|
||||||
# endif
|
# endif
|
||||||
|
# define NIMBLE_CPP_SCAN_DUPL_ENABLED \
|
||||||
|
(CONFIG_BTDM_BLE_SCAN_DUPL || CONFIG_BT_LE_SCAN_DUPL || CONFIG_BT_CTRL_BLE_SCAN_DUPL)
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
# if defined(CONFIG_NIMBLE_CPP_IDF)
|
# if defined(CONFIG_NIMBLE_CPP_IDF)
|
||||||
@@ -40,32 +43,36 @@
|
|||||||
# include <string>
|
# include <string>
|
||||||
# include <vector>
|
# include <vector>
|
||||||
|
|
||||||
# if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
|
# if MYNEWT_VAL(BLE_ROLE_CENTRAL)
|
||||||
# include <array>
|
# include <array>
|
||||||
class NimBLEClient;
|
class NimBLEClient;
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
# if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
|
# if MYNEWT_VAL(BLE_ROLE_OBSERVER)
|
||||||
class NimBLEScan;
|
class NimBLEScan;
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
# if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
|
# if MYNEWT_VAL(BLE_ROLE_BROADCASTER)
|
||||||
# if CONFIG_BT_NIMBLE_EXT_ADV
|
# if MYNEWT_VAL(BLE_EXT_ADV)
|
||||||
class NimBLEExtAdvertising;
|
class NimBLEExtAdvertising;
|
||||||
# else
|
# else
|
||||||
class NimBLEAdvertising;
|
class NimBLEAdvertising;
|
||||||
# endif
|
# endif
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
# if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
|
# if MYNEWT_VAL(BLE_ROLE_PERIPHERAL)
|
||||||
class NimBLEServer;
|
class NimBLEServer;
|
||||||
|
# if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM)
|
||||||
|
class NimBLEL2CAPServer;
|
||||||
|
# endif
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
# if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) || defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
|
# if MYNEWT_VAL(BLE_ROLE_PERIPHERAL) || MYNEWT_VAL(BLE_ROLE_CENTRAL)
|
||||||
class NimBLEConnInfo;
|
class NimBLEConnInfo;
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
class NimBLEAddress;
|
class NimBLEAddress;
|
||||||
|
class NimBLEDeviceCallbacks;
|
||||||
|
|
||||||
# define BLEDevice NimBLEDevice
|
# define BLEDevice NimBLEDevice
|
||||||
# define BLEClient NimBLEClient
|
# define BLEClient NimBLEClient
|
||||||
@@ -94,12 +101,15 @@ class NimBLEAddress;
|
|||||||
# define BLEEddystoneTLM NimBLEEddystoneTLM
|
# define BLEEddystoneTLM NimBLEEddystoneTLM
|
||||||
# define BLEEddystoneURL NimBLEEddystoneURL
|
# define BLEEddystoneURL NimBLEEddystoneURL
|
||||||
# define BLEConnInfo NimBLEConnInfo
|
# 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
|
enum class NimBLETxPowerType { All = 0, Advertise = 1, Scan = 2, Connection = 3 };
|
||||||
# define NIMBLE_MAX_CONNECTIONS CONFIG_BT_NIMBLE_MAX_CONNECTIONS
|
|
||||||
# else
|
|
||||||
# define NIMBLE_MAX_CONNECTIONS CONFIG_NIMBLE_MAX_CONNECTIONS
|
|
||||||
# endif
|
|
||||||
|
|
||||||
typedef int (*gap_event_handler)(ble_gap_event* event, void* arg);
|
typedef int (*gap_event_handler)(ble_gap_event* event, void* arg);
|
||||||
|
|
||||||
@@ -114,6 +124,7 @@ class NimBLEDevice {
|
|||||||
static bool isInitialized();
|
static bool isInitialized();
|
||||||
static NimBLEAddress getAddress();
|
static NimBLEAddress getAddress();
|
||||||
static std::string toString();
|
static std::string toString();
|
||||||
|
static const char* getVersion();
|
||||||
static bool whiteListAdd(const NimBLEAddress& address);
|
static bool whiteListAdd(const NimBLEAddress& address);
|
||||||
static bool whiteListRemove(const NimBLEAddress& address);
|
static bool whiteListRemove(const NimBLEAddress& address);
|
||||||
static bool onWhiteList(const NimBLEAddress& address);
|
static bool onWhiteList(const NimBLEAddress& address);
|
||||||
@@ -122,9 +133,11 @@ class NimBLEDevice {
|
|||||||
static bool setOwnAddrType(uint8_t type);
|
static bool setOwnAddrType(uint8_t type);
|
||||||
static bool setOwnAddr(const NimBLEAddress& addr);
|
static bool setOwnAddr(const NimBLEAddress& addr);
|
||||||
static bool setOwnAddr(const uint8_t* addr);
|
static bool setOwnAddr(const uint8_t* addr);
|
||||||
|
static void setDeviceCallbacks(NimBLEDeviceCallbacks* cb);
|
||||||
static void setScanDuplicateCacheSize(uint16_t cacheSize);
|
static void setScanDuplicateCacheSize(uint16_t cacheSize);
|
||||||
static void setScanFilterMode(uint8_t type);
|
static void setScanFilterMode(uint8_t type);
|
||||||
static bool setCustomGapHandler(gap_event_handler handler);
|
static void setScanDuplicateCacheResetTime(uint16_t time);
|
||||||
|
static bool setCustomGapHandler(gap_event_handler handler, void* arg = nullptr);
|
||||||
static void setSecurityAuth(bool bonding, bool mitm, bool sc);
|
static void setSecurityAuth(bool bonding, bool mitm, bool sc);
|
||||||
static void setSecurityAuth(uint8_t auth);
|
static void setSecurityAuth(uint8_t auth);
|
||||||
static void setSecurityIOCap(uint8_t iocap);
|
static void setSecurityIOCap(uint8_t iocap);
|
||||||
@@ -138,8 +151,9 @@ class NimBLEDevice {
|
|||||||
static void onReset(int reason);
|
static void onReset(int reason);
|
||||||
static void onSync(void);
|
static void onSync(void);
|
||||||
static void host_task(void* param);
|
static void host_task(void* param);
|
||||||
static int getPower();
|
static int getPower(NimBLETxPowerType type = NimBLETxPowerType::All);
|
||||||
static bool setPower(int8_t dbm);
|
static bool setPower(int8_t dbm, NimBLETxPowerType type = NimBLETxPowerType::All);
|
||||||
|
static bool setDefaultPhy(uint8_t txPhyMask, uint8_t rxPhyMask);
|
||||||
|
|
||||||
# ifdef ESP_PLATFORM
|
# ifdef ESP_PLATFORM
|
||||||
# ifndef CONFIG_IDF_TARGET_ESP32P4
|
# ifndef CONFIG_IDF_TARGET_ESP32P4
|
||||||
@@ -148,39 +162,39 @@ class NimBLEDevice {
|
|||||||
# endif
|
# endif
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
# if CONFIG_BT_NIMBLE_EXT_ADV
|
# if MYNEWT_VAL(BLE_ROLE_OBSERVER)
|
||||||
static bool setDefaultPhy(uint8_t txPhyMask, uint8_t rxPhyMask);
|
|
||||||
# endif
|
|
||||||
|
|
||||||
# if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
|
|
||||||
static NimBLEScan* getScan();
|
static NimBLEScan* getScan();
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
# if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
|
# if MYNEWT_VAL(BLE_ROLE_PERIPHERAL)
|
||||||
static NimBLEServer* createServer();
|
static NimBLEServer* createServer();
|
||||||
static NimBLEServer* getServer();
|
static NimBLEServer* getServer();
|
||||||
|
# if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM)
|
||||||
|
static NimBLEL2CAPServer* createL2CAPServer();
|
||||||
|
static NimBLEL2CAPServer* getL2CAPServer();
|
||||||
|
# endif
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
# if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) || defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
|
# if MYNEWT_VAL(BLE_ROLE_PERIPHERAL) || MYNEWT_VAL(BLE_ROLE_CENTRAL)
|
||||||
static bool injectConfirmPasskey(const NimBLEConnInfo& peerInfo, bool accept);
|
static bool injectConfirmPasskey(const NimBLEConnInfo& peerInfo, bool accept);
|
||||||
static bool injectPassKey(const NimBLEConnInfo& peerInfo, uint32_t pin);
|
static bool injectPassKey(const NimBLEConnInfo& peerInfo, uint32_t pin);
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
# if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
|
# if MYNEWT_VAL(BLE_ROLE_BROADCASTER)
|
||||||
# if CONFIG_BT_NIMBLE_EXT_ADV
|
# if MYNEWT_VAL(BLE_EXT_ADV)
|
||||||
static NimBLEExtAdvertising* getAdvertising();
|
static NimBLEExtAdvertising* getAdvertising();
|
||||||
static bool startAdvertising(uint8_t instId, int duration = 0, int maxEvents = 0);
|
static bool startAdvertising(uint8_t instId, int duration = 0, int maxEvents = 0);
|
||||||
static bool stopAdvertising(uint8_t instId);
|
static bool stopAdvertising(uint8_t instId);
|
||||||
static bool stopAdvertising();
|
static bool stopAdvertising();
|
||||||
# endif
|
# endif
|
||||||
# if !CONFIG_BT_NIMBLE_EXT_ADV || defined(_DOXYGEN_)
|
# if !MYNEWT_VAL(BLE_EXT_ADV) || defined(_DOXYGEN_)
|
||||||
static NimBLEAdvertising* getAdvertising();
|
static NimBLEAdvertising* getAdvertising();
|
||||||
static bool startAdvertising(uint32_t duration = 0);
|
static bool startAdvertising(uint32_t duration = 0);
|
||||||
static bool stopAdvertising();
|
static bool stopAdvertising();
|
||||||
# endif
|
# endif
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
# if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
|
# if MYNEWT_VAL(BLE_ROLE_CENTRAL)
|
||||||
static NimBLEClient* createClient();
|
static NimBLEClient* createClient();
|
||||||
static NimBLEClient* createClient(const NimBLEAddress& peerAddress);
|
static NimBLEClient* createClient(const NimBLEAddress& peerAddress);
|
||||||
static bool deleteClient(NimBLEClient* pClient);
|
static bool deleteClient(NimBLEClient* pClient);
|
||||||
@@ -191,7 +205,7 @@ class NimBLEDevice {
|
|||||||
static std::vector<NimBLEClient*> getConnectedClients();
|
static std::vector<NimBLEClient*> getConnectedClients();
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
# if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL) || defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
|
# if MYNEWT_VAL(BLE_ROLE_CENTRAL) || MYNEWT_VAL(BLE_ROLE_PERIPHERAL)
|
||||||
static bool deleteBond(const NimBLEAddress& address);
|
static bool deleteBond(const NimBLEAddress& address);
|
||||||
static int getNumBonds();
|
static int getNumBonds();
|
||||||
static bool isBonded(const NimBLEAddress& address);
|
static bool isBonded(const NimBLEAddress& address);
|
||||||
@@ -206,81 +220,121 @@ class NimBLEDevice {
|
|||||||
static ble_gap_event_listener m_listener;
|
static ble_gap_event_listener m_listener;
|
||||||
static uint8_t m_ownAddrType;
|
static uint8_t m_ownAddrType;
|
||||||
static std::vector<NimBLEAddress> m_whiteList;
|
static std::vector<NimBLEAddress> m_whiteList;
|
||||||
|
static NimBLEDeviceCallbacks* m_pDeviceCallbacks;
|
||||||
|
static NimBLEDeviceCallbacks defaultDeviceCallbacks;
|
||||||
|
|
||||||
# if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
|
# if MYNEWT_VAL(BLE_ROLE_OBSERVER)
|
||||||
static NimBLEScan* m_pScan;
|
static NimBLEScan* m_pScan;
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
# if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
|
# if MYNEWT_VAL(BLE_ROLE_PERIPHERAL)
|
||||||
static NimBLEServer* m_pServer;
|
static NimBLEServer* m_pServer;
|
||||||
|
# if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM)
|
||||||
|
static NimBLEL2CAPServer* m_pL2CAPServer;
|
||||||
|
# endif
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
# if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
|
# if MYNEWT_VAL(BLE_ROLE_BROADCASTER)
|
||||||
# if CONFIG_BT_NIMBLE_EXT_ADV
|
# if MYNEWT_VAL(BLE_EXT_ADV)
|
||||||
static NimBLEExtAdvertising* m_bleAdvertising;
|
static NimBLEExtAdvertising* m_bleAdvertising;
|
||||||
# else
|
# else
|
||||||
static NimBLEAdvertising* m_bleAdvertising;
|
static NimBLEAdvertising* m_bleAdvertising;
|
||||||
# endif
|
# endif
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
# if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
|
# if MYNEWT_VAL(BLE_ROLE_CENTRAL)
|
||||||
static std::array<NimBLEClient*, NIMBLE_MAX_CONNECTIONS> m_pClients;
|
static std::array<NimBLEClient*, MYNEWT_VAL(BLE_MAX_CONNECTIONS)> m_pClients;
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
# ifdef ESP_PLATFORM
|
# ifdef ESP_PLATFORM
|
||||||
# ifdef CONFIG_BTDM_BLE_SCAN_DUPL
|
# if NIMBLE_CPP_SCAN_DUPL_ENABLED
|
||||||
static uint16_t m_scanDuplicateSize;
|
static uint16_t m_scanDuplicateSize;
|
||||||
static uint8_t m_scanFilterMode;
|
static uint8_t m_scanFilterMode;
|
||||||
|
static uint16_t m_scanDuplicateResetTime;
|
||||||
# endif
|
# endif
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
# if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
|
# if MYNEWT_VAL(BLE_ROLE_CENTRAL)
|
||||||
friend class NimBLEClient;
|
friend class NimBLEClient;
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
# if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
|
# if MYNEWT_VAL(BLE_ROLE_OBSERVER)
|
||||||
friend class NimBLEScan;
|
friend class NimBLEScan;
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
# if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
|
# if MYNEWT_VAL(BLE_ROLE_PERIPHERAL)
|
||||||
friend class NimBLEServer;
|
friend class NimBLEServer;
|
||||||
friend class NimBLECharacteristic;
|
friend class NimBLECharacteristic;
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
# if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
|
# if MYNEWT_VAL(BLE_ROLE_BROADCASTER)
|
||||||
friend class NimBLEAdvertising;
|
friend class NimBLEAdvertising;
|
||||||
# if CONFIG_BT_NIMBLE_EXT_ADV
|
# if MYNEWT_VAL(BLE_EXT_ADV)
|
||||||
friend class NimBLEExtAdvertising;
|
friend class NimBLEExtAdvertising;
|
||||||
friend class NimBLEExtAdvertisement;
|
friend class NimBLEExtAdvertisement;
|
||||||
# endif
|
# endif
|
||||||
# endif
|
# endif
|
||||||
};
|
};
|
||||||
|
|
||||||
# if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
|
# if MYNEWT_VAL(BLE_ROLE_CENTRAL)
|
||||||
# include "NimBLEClient.h"
|
# include "NimBLEClient.h"
|
||||||
# include "NimBLERemoteService.h"
|
# include "NimBLERemoteService.h"
|
||||||
# include "NimBLERemoteCharacteristic.h"
|
# include "NimBLERemoteCharacteristic.h"
|
||||||
# include "NimBLERemoteDescriptor.h"
|
# include "NimBLERemoteDescriptor.h"
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
# if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
|
# if MYNEWT_VAL(BLE_ROLE_OBSERVER)
|
||||||
# include "NimBLEScan.h"
|
# include "NimBLEScan.h"
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
# if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
|
# if MYNEWT_VAL(BLE_ROLE_PERIPHERAL)
|
||||||
# include "NimBLEServer.h"
|
# include "NimBLEServer.h"
|
||||||
# include "NimBLEService.h"
|
# include "NimBLEService.h"
|
||||||
# include "NimBLECharacteristic.h"
|
# include "NimBLECharacteristic.h"
|
||||||
# include "NimBLEDescriptor.h"
|
# include "NimBLEDescriptor.h"
|
||||||
|
# if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM)
|
||||||
|
# include "NimBLEL2CAPServer.h"
|
||||||
|
# include "NimBLEL2CAPChannel.h"
|
||||||
|
# endif
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
# if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
|
# if MYNEWT_VAL(BLE_ROLE_BROADCASTER)
|
||||||
# if CONFIG_BT_NIMBLE_EXT_ADV
|
# if MYNEWT_VAL(BLE_EXT_ADV)
|
||||||
# include "NimBLEExtAdvertising.h"
|
# include "NimBLEExtAdvertising.h"
|
||||||
# else
|
# else
|
||||||
# include "NimBLEAdvertising.h"
|
# include "NimBLEAdvertising.h"
|
||||||
# endif
|
# endif
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
#endif // CONFIG_BT_ENABLED
|
# if MYNEWT_VAL(BLE_ROLE_CENTRAL) || MYNEWT_VAL(BLE_ROLE_PERIPHERAL)
|
||||||
|
# include "NimBLEConnInfo.h"
|
||||||
|
# include "NimBLEStream.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_NIMBLE_ENABLED
|
||||||
#endif // NIMBLE_CPP_DEVICE_H_
|
#endif // NIMBLE_CPP_DEVICE_H_
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
|
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
|
||||||
* esp-nimble-cpp, NimBLE-Arduino contributors.
|
* esp-nimble-cpp, NimBLE-Arduino contributors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@@ -15,10 +15,9 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "nimconfig.h"
|
#include "NimBLEEddystoneTLM.h"
|
||||||
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
|
#if CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_BROADCASTER)
|
||||||
|
|
||||||
# include "NimBLEEddystoneTLM.h"
|
|
||||||
# include "NimBLEUUID.h"
|
# include "NimBLEUUID.h"
|
||||||
# include "NimBLELog.h"
|
# include "NimBLELog.h"
|
||||||
|
|
||||||
@@ -216,4 +215,4 @@ void NimBLEEddystoneTLM::setTime(uint32_t tmil) {
|
|||||||
m_eddystoneData.tmil = tmil;
|
m_eddystoneData.tmil = tmil;
|
||||||
} // setTime
|
} // setTime
|
||||||
|
|
||||||
#endif // CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_BROADCASTER
|
#endif // CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_BROADCASTER)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
|
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
|
||||||
* esp-nimble-cpp, NimBLE-Arduino contributors.
|
* esp-nimble-cpp, NimBLE-Arduino contributors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@@ -18,8 +18,8 @@
|
|||||||
#ifndef NIMBLE_CPP_EDDYSTONETLM_H_
|
#ifndef NIMBLE_CPP_EDDYSTONETLM_H_
|
||||||
#define NIMBLE_CPP_EDDYSTONETLM_H_
|
#define NIMBLE_CPP_EDDYSTONETLM_H_
|
||||||
|
|
||||||
#include "nimconfig.h"
|
#include "syscfg/syscfg.h"
|
||||||
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
|
#if CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_BROADCASTER)
|
||||||
|
|
||||||
class NimBLEUUID;
|
class NimBLEUUID;
|
||||||
|
|
||||||
@@ -66,5 +66,5 @@ class NimBLEEddystoneTLM {
|
|||||||
|
|
||||||
}; // NimBLEEddystoneTLM
|
}; // NimBLEEddystoneTLM
|
||||||
|
|
||||||
#endif // CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_BROADCASTER
|
#endif // CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_BROADCASTER)
|
||||||
#endif // NIMBLE_CPP_EDDYSTONETLM_H_
|
#endif // NIMBLE_CPP_EDDYSTONETLM_H_
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
|
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
|
||||||
* esp-nimble-cpp, NimBLE-Arduino contributors.
|
* esp-nimble-cpp, NimBLE-Arduino contributors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@@ -15,8 +15,8 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "nimconfig.h"
|
#include "NimBLEExtAdvertising.h"
|
||||||
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER) && CONFIG_BT_NIMBLE_EXT_ADV
|
#if CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_BROADCASTER) && MYNEWT_VAL(BLE_EXT_ADV)
|
||||||
|
|
||||||
# if defined(CONFIG_NIMBLE_CPP_IDF)
|
# if defined(CONFIG_NIMBLE_CPP_IDF)
|
||||||
# include "services/gap/ble_svc_gap.h"
|
# include "services/gap/ble_svc_gap.h"
|
||||||
@@ -24,7 +24,6 @@
|
|||||||
# include "nimble/nimble/host/services/gap/include/services/gap/ble_svc_gap.h"
|
# include "nimble/nimble/host/services/gap/include/services/gap/ble_svc_gap.h"
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
# include "NimBLEExtAdvertising.h"
|
|
||||||
# include "NimBLEDevice.h"
|
# include "NimBLEDevice.h"
|
||||||
# include "NimBLEServer.h"
|
# include "NimBLEServer.h"
|
||||||
# include "NimBLEUtils.h"
|
# include "NimBLEUtils.h"
|
||||||
@@ -39,7 +38,7 @@ static const char* LOG_TAG = "NimBLEExtAdvertising";
|
|||||||
NimBLEExtAdvertising::NimBLEExtAdvertising()
|
NimBLEExtAdvertising::NimBLEExtAdvertising()
|
||||||
: m_deleteCallbacks{false},
|
: m_deleteCallbacks{false},
|
||||||
m_pCallbacks{&defaultCallbacks},
|
m_pCallbacks{&defaultCallbacks},
|
||||||
m_advStatus(CONFIG_BT_NIMBLE_MAX_EXT_ADV_INSTANCES + 1, false) {}
|
m_advStatus(MYNEWT_VAL(BLE_MULTI_ADV_INSTANCES) + 1, false) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Destructor: deletes callback instances if requested.
|
* @brief Destructor: deletes callback instances if requested.
|
||||||
@@ -69,7 +68,7 @@ bool NimBLEExtAdvertising::setInstanceData(uint8_t instId, NimBLEExtAdvertisemen
|
|||||||
adv.m_params.scan_req_notif = false;
|
adv.m_params.scan_req_notif = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
# if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
|
# if MYNEWT_VAL(BLE_ROLE_PERIPHERAL)
|
||||||
NimBLEServer* pServer = NimBLEDevice::getServer();
|
NimBLEServer* pServer = NimBLEDevice::getServer();
|
||||||
if (pServer != nullptr) {
|
if (pServer != nullptr) {
|
||||||
pServer->start(); // make sure the GATT server is ready before advertising
|
pServer->start(); // make sure the GATT server is ready before advertising
|
||||||
@@ -359,7 +358,7 @@ NimBLEExtAdvertisement::NimBLEExtAdvertisement(uint8_t priPhy, uint8_t secPhy) {
|
|||||||
m_params.own_addr_type = NimBLEDevice::m_ownAddrType;
|
m_params.own_addr_type = NimBLEDevice::m_ownAddrType;
|
||||||
m_params.primary_phy = priPhy;
|
m_params.primary_phy = priPhy;
|
||||||
m_params.secondary_phy = secPhy;
|
m_params.secondary_phy = secPhy;
|
||||||
m_params.tx_power = 127;
|
m_params.tx_power = NimBLEDevice::getPower(NimBLETxPowerType::Advertise);
|
||||||
} // NimBLEExtAdvertisement
|
} // NimBLEExtAdvertisement
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -392,7 +391,7 @@ void NimBLEExtAdvertisement::setTxPower(int8_t dbm) {
|
|||||||
* @param [in] enable True = connectable.
|
* @param [in] enable True = connectable.
|
||||||
*/
|
*/
|
||||||
void NimBLEExtAdvertisement::setConnectable(bool enable) {
|
void NimBLEExtAdvertisement::setConnectable(bool enable) {
|
||||||
# if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
|
# if MYNEWT_VAL(BLE_ROLE_PERIPHERAL)
|
||||||
m_params.connectable = enable;
|
m_params.connectable = enable;
|
||||||
# endif
|
# endif
|
||||||
} // setConnectable
|
} // setConnectable
|
||||||
@@ -532,7 +531,7 @@ void NimBLEExtAdvertisement::clearData() {
|
|||||||
* @details This will completely replace any data that was previously set.
|
* @details This will completely replace any data that was previously set.
|
||||||
*/
|
*/
|
||||||
bool NimBLEExtAdvertisement::setData(const uint8_t* data, size_t length) {
|
bool NimBLEExtAdvertisement::setData(const uint8_t* data, size_t length) {
|
||||||
if (length > CONFIG_BT_NIMBLE_MAX_EXT_ADV_DATA_LEN) {
|
if (length > MYNEWT_VAL(BLE_EXT_ADV_MAX_SIZE)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -547,7 +546,7 @@ bool NimBLEExtAdvertisement::setData(const uint8_t* data, size_t length) {
|
|||||||
* @return True if successful, false if the data is too large.
|
* @return True if successful, false if the data is too large.
|
||||||
*/
|
*/
|
||||||
bool 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) {
|
if (m_payload.size() + length > MYNEWT_VAL(BLE_EXT_ADV_MAX_SIZE)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -561,7 +560,7 @@ bool NimBLEExtAdvertisement::addData(const uint8_t* data, size_t length) {
|
|||||||
* @return True if successful, false if the data is too large.
|
* @return True if successful, false if the data is too large.
|
||||||
*/
|
*/
|
||||||
bool NimBLEExtAdvertisement::addData(const std::string& data) {
|
bool NimBLEExtAdvertisement::addData(const std::string& data) {
|
||||||
if (m_payload.size() + data.length() > CONFIG_BT_NIMBLE_MAX_EXT_ADV_DATA_LEN) {
|
if (m_payload.size() + data.length() > MYNEWT_VAL(BLE_EXT_ADV_MAX_SIZE)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -617,6 +616,11 @@ bool NimBLEExtAdvertisement::setFlags(uint8_t flag) {
|
|||||||
* @return True if successful.
|
* @return True if successful.
|
||||||
*/
|
*/
|
||||||
bool NimBLEExtAdvertisement::setManufacturerData(const uint8_t* data, size_t length) {
|
bool NimBLEExtAdvertisement::setManufacturerData(const uint8_t* data, size_t length) {
|
||||||
|
if (length > 0xFF - 1) {
|
||||||
|
NIMBLE_LOGE(LOG_TAG, "Manufacturer data too long!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
uint8_t header[2];
|
uint8_t header[2];
|
||||||
header[0] = length + 1;
|
header[0] = length + 1;
|
||||||
header[1] = BLE_HS_ADV_TYPE_MFG_DATA;
|
header[1] = BLE_HS_ADV_TYPE_MFG_DATA;
|
||||||
@@ -653,6 +657,11 @@ bool NimBLEExtAdvertisement::setManufacturerData(const std::vector<uint8_t>& dat
|
|||||||
* @return True if successful.
|
* @return True if successful.
|
||||||
*/
|
*/
|
||||||
bool NimBLEExtAdvertisement::setURI(const std::string& uri) {
|
bool NimBLEExtAdvertisement::setURI(const std::string& uri) {
|
||||||
|
if (uri.length() > 0xFF - 1) {
|
||||||
|
NIMBLE_LOGE(LOG_TAG, "URI too long!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
uint8_t header[2];
|
uint8_t header[2];
|
||||||
header[0] = uri.length() + 1;
|
header[0] = uri.length() + 1;
|
||||||
header[1] = BLE_HS_ADV_TYPE_URI;
|
header[1] = BLE_HS_ADV_TYPE_URI;
|
||||||
@@ -671,6 +680,11 @@ bool NimBLEExtAdvertisement::setURI(const std::string& uri) {
|
|||||||
* @return True if successful.
|
* @return True if successful.
|
||||||
*/
|
*/
|
||||||
bool NimBLEExtAdvertisement::setName(const std::string& name, bool isComplete) {
|
bool NimBLEExtAdvertisement::setName(const std::string& name, bool isComplete) {
|
||||||
|
if (name.length() > 0xFF - 1) {
|
||||||
|
NIMBLE_LOGE(LOG_TAG, "Name too long!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
uint8_t header[2];
|
uint8_t header[2];
|
||||||
header[0] = name.length() + 1;
|
header[0] = name.length() + 1;
|
||||||
header[1] = isComplete ? BLE_HS_ADV_TYPE_COMP_NAME : BLE_HS_ADV_TYPE_INCOMP_NAME;
|
header[1] = isComplete ? BLE_HS_ADV_TYPE_COMP_NAME : BLE_HS_ADV_TYPE_INCOMP_NAME;
|
||||||
@@ -701,6 +715,7 @@ bool NimBLEExtAdvertisement::addServiceUUID(const NimBLEUUID& serviceUUID) {
|
|||||||
type = BLE_HS_ADV_TYPE_COMP_UUIDS128;
|
type = BLE_HS_ADV_TYPE_COMP_UUIDS128;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
NIMBLE_LOGE(LOG_TAG, "Cannot add UUID, invalid size!");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -710,11 +725,12 @@ bool NimBLEExtAdvertisement::addServiceUUID(const NimBLEUUID& serviceUUID) {
|
|||||||
length += 2;
|
length += 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (length + getDataSize() > CONFIG_BT_NIMBLE_MAX_EXT_ADV_DATA_LEN) {
|
if (length + getDataSize() > MYNEWT_VAL(BLE_EXT_ADV_MAX_SIZE)) {
|
||||||
|
NIMBLE_LOGE(LOG_TAG, "Cannot add UUID, data length exceeded!");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t data[31];
|
uint8_t data[BLE_HS_ADV_MAX_SZ];
|
||||||
const uint8_t* uuid = serviceUUID.getValue();
|
const uint8_t* uuid = serviceUUID.getValue();
|
||||||
if (dataLoc == -1) {
|
if (dataLoc == -1) {
|
||||||
data[0] = 1 + bytes;
|
data[0] = 1 + bytes;
|
||||||
@@ -756,6 +772,7 @@ bool NimBLEExtAdvertisement::removeServiceUUID(const NimBLEUUID& serviceUUID) {
|
|||||||
type = BLE_HS_ADV_TYPE_COMP_UUIDS128;
|
type = BLE_HS_ADV_TYPE_COMP_UUIDS128;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
NIMBLE_LOGE(LOG_TAG, "Cannot remove UUID, invalid size!");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -878,6 +895,7 @@ bool NimBLEExtAdvertisement::setServices(bool complete, uint8_t size, const std:
|
|||||||
header[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;
|
break;
|
||||||
default:
|
default:
|
||||||
|
NIMBLE_LOGE(LOG_TAG, "Cannot set services, invalid size!");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -914,9 +932,13 @@ bool NimBLEExtAdvertisement::setServices(bool complete, uint8_t size, const std:
|
|||||||
*/
|
*/
|
||||||
bool NimBLEExtAdvertisement::setServiceData(const NimBLEUUID& uuid, const uint8_t* data, size_t length) {
|
bool NimBLEExtAdvertisement::setServiceData(const NimBLEUUID& uuid, const uint8_t* data, size_t length) {
|
||||||
uint8_t uuidBytes = uuid.bitSize() / 8;
|
uint8_t uuidBytes = uuid.bitSize() / 8;
|
||||||
uint8_t sDataLen = 2 + uuidBytes + length;
|
if (length + uuidBytes + 2 > 0xFF) {
|
||||||
|
NIMBLE_LOGE(LOG_TAG, "Service data too long!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (m_payload.size() + sDataLen > CONFIG_BT_NIMBLE_MAX_EXT_ADV_DATA_LEN) {
|
uint8_t sDataLen = 2 + uuidBytes + length;
|
||||||
|
if (m_payload.size() + sDataLen > MYNEWT_VAL(BLE_EXT_ADV_MAX_SIZE)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -932,6 +954,7 @@ bool NimBLEExtAdvertisement::setServiceData(const NimBLEUUID& uuid, const uint8_
|
|||||||
type = BLE_HS_ADV_TYPE_SVC_DATA_UUID128;
|
type = BLE_HS_ADV_TYPE_SVC_DATA_UUID128;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
NIMBLE_LOGE(LOG_TAG, "Cannot set service data, invalid size!");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1009,8 +1032,22 @@ bool NimBLEExtAdvertisement::setPreferredParams(uint16_t minInterval, uint16_t m
|
|||||||
/**
|
/**
|
||||||
* @brief Adds Tx power level to the advertisement data.
|
* @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;
|
m_params.include_tx_power = 1;
|
||||||
|
return true;
|
||||||
} // addTxPower
|
} // addTxPower
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1072,4 +1109,4 @@ std::string NimBLEExtAdvertisement::toString() const {
|
|||||||
return str;
|
return str;
|
||||||
} // toString
|
} // toString
|
||||||
|
|
||||||
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_BROADCASTER && CONFIG_BT_NIMBLE_EXT_ADV */
|
#endif // CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_BROADCASTER) && MYNEWT_VAL(BLE_EXT_ADV)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
|
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
|
||||||
* esp-nimble-cpp, NimBLE-Arduino contributors.
|
* esp-nimble-cpp, NimBLE-Arduino contributors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@@ -18,8 +18,8 @@
|
|||||||
#ifndef NIMBLE_CPP_EXTADVERTISING_H_
|
#ifndef NIMBLE_CPP_EXTADVERTISING_H_
|
||||||
#define NIMBLE_CPP_EXTADVERTISING_H_
|
#define NIMBLE_CPP_EXTADVERTISING_H_
|
||||||
|
|
||||||
#include "nimconfig.h"
|
#include "syscfg/syscfg.h"
|
||||||
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER) && CONFIG_BT_NIMBLE_EXT_ADV
|
#if CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_BROADCASTER) && MYNEWT_VAL(BLE_EXT_ADV)
|
||||||
|
|
||||||
# if defined(CONFIG_NIMBLE_CPP_IDF)
|
# if defined(CONFIG_NIMBLE_CPP_IDF)
|
||||||
# include "host/ble_gap.h"
|
# include "host/ble_gap.h"
|
||||||
@@ -46,34 +46,33 @@ class NimBLEUUID;
|
|||||||
class NimBLEExtAdvertisement {
|
class NimBLEExtAdvertisement {
|
||||||
public:
|
public:
|
||||||
NimBLEExtAdvertisement(uint8_t priPhy = BLE_HCI_LE_PHY_1M, uint8_t secPhy = BLE_HCI_LE_PHY_1M);
|
NimBLEExtAdvertisement(uint8_t priPhy = BLE_HCI_LE_PHY_1M, uint8_t secPhy = BLE_HCI_LE_PHY_1M);
|
||||||
bool setAppearance(uint16_t appearance);
|
bool setAppearance(uint16_t appearance);
|
||||||
bool addServiceUUID(const NimBLEUUID& serviceUUID);
|
bool addServiceUUID(const NimBLEUUID& serviceUUID);
|
||||||
bool addServiceUUID(const char* serviceUUID);
|
bool addServiceUUID(const char* serviceUUID);
|
||||||
bool removeServiceUUID(const NimBLEUUID& serviceUUID);
|
bool removeServiceUUID(const NimBLEUUID& serviceUUID);
|
||||||
bool removeServiceUUID(const char* serviceUUID);
|
bool removeServiceUUID(const char* serviceUUID);
|
||||||
bool removeServices();
|
bool removeServices();
|
||||||
bool setCompleteServices(const NimBLEUUID& uuid);
|
bool setCompleteServices(const NimBLEUUID& uuid);
|
||||||
bool setCompleteServices16(const std::vector<NimBLEUUID>& uuids);
|
bool setCompleteServices16(const std::vector<NimBLEUUID>& uuids);
|
||||||
bool setCompleteServices32(const std::vector<NimBLEUUID>& uuids);
|
bool setCompleteServices32(const std::vector<NimBLEUUID>& uuids);
|
||||||
bool setFlags(uint8_t flag);
|
bool setFlags(uint8_t flag);
|
||||||
bool setManufacturerData(const uint8_t* data, size_t length);
|
bool setManufacturerData(const uint8_t* data, size_t length);
|
||||||
bool setManufacturerData(const std::string& data);
|
bool setManufacturerData(const std::string& data);
|
||||||
bool setManufacturerData(const std::vector<uint8_t>& data);
|
bool setManufacturerData(const std::vector<uint8_t>& data);
|
||||||
bool setURI(const std::string& uri);
|
bool setURI(const std::string& uri);
|
||||||
bool setName(const std::string& name, bool isComplete = true);
|
bool setName(const std::string& name, bool isComplete = true);
|
||||||
bool setPartialServices(const NimBLEUUID& uuid);
|
bool setPartialServices(const NimBLEUUID& uuid);
|
||||||
bool setPartialServices16(const std::vector<NimBLEUUID>& uuids);
|
bool setPartialServices16(const std::vector<NimBLEUUID>& uuids);
|
||||||
bool setPartialServices32(const std::vector<NimBLEUUID>& uuids);
|
bool setPartialServices32(const std::vector<NimBLEUUID>& uuids);
|
||||||
bool setServiceData(const NimBLEUUID& uuid, const uint8_t* data, size_t length);
|
bool setServiceData(const NimBLEUUID& uuid, const uint8_t* data, size_t length);
|
||||||
bool setServiceData(const NimBLEUUID& uuid, const std::string& data);
|
bool setServiceData(const NimBLEUUID& uuid, const std::string& data);
|
||||||
bool setServiceData(const NimBLEUUID& uuid, const std::vector<uint8_t>& data);
|
bool setServiceData(const NimBLEUUID& uuid, const std::vector<uint8_t>& data);
|
||||||
bool setShortName(const std::string& name);
|
bool setShortName(const std::string& name);
|
||||||
bool setData(const uint8_t* data, size_t length);
|
bool setData(const uint8_t* data, size_t length);
|
||||||
bool addData(const uint8_t* data, size_t length);
|
bool addData(const uint8_t* data, size_t length);
|
||||||
bool addData(const std::string& data);
|
bool addData(const std::string& data);
|
||||||
bool setPreferredParams(uint16_t min, uint16_t max);
|
bool setPreferredParams(uint16_t min, uint16_t max);
|
||||||
|
bool addTxPower();
|
||||||
void addTxPower();
|
|
||||||
void setLegacyAdvertising(bool enable);
|
void setLegacyAdvertising(bool enable);
|
||||||
void setConnectable(bool enable);
|
void setConnectable(bool enable);
|
||||||
void setScannable(bool enable);
|
void setScannable(bool enable);
|
||||||
@@ -160,5 +159,5 @@ class NimBLEExtAdvertisingCallbacks {
|
|||||||
virtual void onScanRequest(NimBLEExtAdvertising* pAdv, uint8_t instId, NimBLEAddress addr);
|
virtual void onScanRequest(NimBLEExtAdvertising* pAdv, uint8_t instId, NimBLEAddress addr);
|
||||||
}; // NimBLEExtAdvertisingCallbacks
|
}; // NimBLEExtAdvertisingCallbacks
|
||||||
|
|
||||||
#endif // CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_BROADCASTER && CONFIG_BT_NIMBLE_EXT_ADV
|
#endif // CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_BROADCASTER) && MYNEWT_VAL(BLE_EXT_ADV)
|
||||||
#endif // NIMBLE_CPP_EXTADVERTISING_H_
|
#endif // NIMBLE_CPP_EXTADVERTISING_H_
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
|
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
|
||||||
* esp-nimble-cpp, NimBLE-Arduino contributors.
|
* esp-nimble-cpp, NimBLE-Arduino contributors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@@ -15,10 +15,9 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "nimconfig.h"
|
#include "NimBLEHIDDevice.h"
|
||||||
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
|
#if CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_BROADCASTER) && MYNEWT_VAL(BLE_ROLE_PERIPHERAL)
|
||||||
|
|
||||||
# include "NimBLEHIDDevice.h"
|
|
||||||
# include "NimBLEServer.h"
|
# include "NimBLEServer.h"
|
||||||
# include "NimBLEService.h"
|
# include "NimBLEService.h"
|
||||||
# include "NimBLE2904.h"
|
# include "NimBLE2904.h"
|
||||||
@@ -85,9 +84,7 @@ void NimBLEHIDDevice::setReportMap(uint8_t* map, uint16_t size) {
|
|||||||
* This function called when all the services have been created.
|
* This function called when all the services have been created.
|
||||||
*/
|
*/
|
||||||
void NimBLEHIDDevice::startServices() {
|
void NimBLEHIDDevice::startServices() {
|
||||||
m_deviceInfoSvc->start();
|
// no-op now, services started by server start.
|
||||||
m_hidSvc->start();
|
|
||||||
m_batterySvc->start();
|
|
||||||
} // startServices
|
} // startServices
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -149,14 +146,35 @@ void NimBLEHIDDevice::setBatteryLevel(uint8_t level, bool notify) {
|
|||||||
}
|
}
|
||||||
} // setBatteryLevel
|
} // setBatteryLevel
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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.
|
||||||
|
*/
|
||||||
|
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 Get the input report characteristic.
|
* @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.
|
* @param [in] reportId Input report ID, the same as in report map for input object related to the characteristic.
|
||||||
* @return A pointer to the input report 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.
|
* @details This will create the characteristic if not already created.
|
||||||
*/
|
*/
|
||||||
NimBLECharacteristic* NimBLEHIDDevice::getInputReport(uint8_t reportId) {
|
NimBLECharacteristic* NimBLEHIDDevice::getInputReport(uint8_t reportId) {
|
||||||
NimBLECharacteristic* inputReportChr = m_hidSvc->getCharacteristic(inputReportChrUuid);
|
NimBLECharacteristic* inputReportChr = locateReportCharacteristicByIdAndType(reportId, 0x01);
|
||||||
if (inputReportChr == nullptr) {
|
if (inputReportChr == nullptr) {
|
||||||
inputReportChr =
|
inputReportChr =
|
||||||
m_hidSvc->createCharacteristic(inputReportChrUuid,
|
m_hidSvc->createCharacteristic(inputReportChrUuid,
|
||||||
@@ -174,11 +192,12 @@ NimBLECharacteristic* NimBLEHIDDevice::getInputReport(uint8_t reportId) {
|
|||||||
/**
|
/**
|
||||||
* @brief Get the output report characteristic.
|
* @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.
|
* @param [in] reportId Output report ID, the same as in report map for output object related to the characteristic.
|
||||||
* @return A pointer to the output report 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.
|
* @details This will create the characteristic if not already created.
|
||||||
*/
|
*/
|
||||||
NimBLECharacteristic* NimBLEHIDDevice::getOutputReport(uint8_t reportId) {
|
NimBLECharacteristic* NimBLEHIDDevice::getOutputReport(uint8_t reportId) {
|
||||||
NimBLECharacteristic* outputReportChr = m_hidSvc->getCharacteristic(inputReportChrUuid);
|
NimBLECharacteristic* outputReportChr = locateReportCharacteristicByIdAndType(reportId, 0x02);
|
||||||
if (outputReportChr == nullptr) {
|
if (outputReportChr == nullptr) {
|
||||||
outputReportChr =
|
outputReportChr =
|
||||||
m_hidSvc->createCharacteristic(inputReportChrUuid,
|
m_hidSvc->createCharacteristic(inputReportChrUuid,
|
||||||
@@ -187,7 +206,6 @@ NimBLECharacteristic* NimBLEHIDDevice::getOutputReport(uint8_t reportId) {
|
|||||||
NimBLEDescriptor* outputReportDsc = outputReportChr->createDescriptor(
|
NimBLEDescriptor* outputReportDsc = outputReportChr->createDescriptor(
|
||||||
featureReportDscUuid,
|
featureReportDscUuid,
|
||||||
NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::READ_ENC | NIMBLE_PROPERTY::WRITE_ENC);
|
NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::READ_ENC | NIMBLE_PROPERTY::WRITE_ENC);
|
||||||
|
|
||||||
uint8_t desc1_val[] = {reportId, 0x02};
|
uint8_t desc1_val[] = {reportId, 0x02};
|
||||||
outputReportDsc->setValue(desc1_val, 2);
|
outputReportDsc->setValue(desc1_val, 2);
|
||||||
}
|
}
|
||||||
@@ -198,11 +216,12 @@ NimBLECharacteristic* NimBLEHIDDevice::getOutputReport(uint8_t reportId) {
|
|||||||
/**
|
/**
|
||||||
* @brief Get the feature report characteristic.
|
* @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.
|
* @param [in] reportId Feature report ID, the same as in report map for feature object related to the characteristic.
|
||||||
* @return A pointer to feature report 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.
|
* @details This will create the characteristic if not already created.
|
||||||
*/
|
*/
|
||||||
NimBLECharacteristic* NimBLEHIDDevice::getFeatureReport(uint8_t reportId) {
|
NimBLECharacteristic* NimBLEHIDDevice::getFeatureReport(uint8_t reportId) {
|
||||||
NimBLECharacteristic* featureReportChr = m_hidSvc->getCharacteristic(inputReportChrUuid);
|
NimBLECharacteristic* featureReportChr = locateReportCharacteristicByIdAndType(reportId, 0x03);
|
||||||
if (featureReportChr == nullptr) {
|
if (featureReportChr == nullptr) {
|
||||||
featureReportChr = m_hidSvc->createCharacteristic(
|
featureReportChr = m_hidSvc->createCharacteristic(
|
||||||
inputReportChrUuid,
|
inputReportChrUuid,
|
||||||
@@ -319,4 +338,4 @@ NimBLEService* NimBLEHIDDevice::getBatteryService() {
|
|||||||
return m_batterySvc;
|
return m_batterySvc;
|
||||||
} // getBatteryService
|
} // getBatteryService
|
||||||
|
|
||||||
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL */
|
#endif // CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_PERIPHERAL)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
|
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
|
||||||
* esp-nimble-cpp, NimBLE-Arduino contributors.
|
* esp-nimble-cpp, NimBLE-Arduino contributors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@@ -18,8 +18,8 @@
|
|||||||
#ifndef NIMBLE_CPP_HIDDEVICE_H_
|
#ifndef NIMBLE_CPP_HIDDEVICE_H_
|
||||||
#define NIMBLE_CPP_HIDDEVICE_H_
|
#define NIMBLE_CPP_HIDDEVICE_H_
|
||||||
|
|
||||||
#include "nimconfig.h"
|
#include "syscfg/syscfg.h"
|
||||||
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
|
#if CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_BROADCASTER) && MYNEWT_VAL(BLE_ROLE_PERIPHERAL)
|
||||||
|
|
||||||
# include <stdint.h>
|
# include <stdint.h>
|
||||||
# include <string>
|
# include <string>
|
||||||
@@ -49,7 +49,8 @@ class NimBLEHIDDevice {
|
|||||||
NimBLEHIDDevice(NimBLEServer* server);
|
NimBLEHIDDevice(NimBLEServer* server);
|
||||||
|
|
||||||
void setReportMap(uint8_t* map, uint16_t);
|
void setReportMap(uint8_t* map, uint16_t);
|
||||||
void startServices();
|
void startServices() __attribute__((deprecated("Services are now started by the server when start() is called, "
|
||||||
|
"this function is no longer needed and will be removed in a future release.")));
|
||||||
bool setManufacturer(const std::string& name);
|
bool setManufacturer(const std::string& name);
|
||||||
void setPnp(uint8_t sig, uint16_t vid, uint16_t pid, uint16_t version);
|
void setPnp(uint8_t sig, uint16_t vid, uint16_t pid, uint16_t version);
|
||||||
void setHidInfo(uint8_t country, uint8_t flags);
|
void setHidInfo(uint8_t country, uint8_t flags);
|
||||||
@@ -81,7 +82,9 @@ class NimBLEHIDDevice {
|
|||||||
NimBLECharacteristic* m_hidControlChr{nullptr}; // 0x2a4c
|
NimBLECharacteristic* m_hidControlChr{nullptr}; // 0x2a4c
|
||||||
NimBLECharacteristic* m_protocolModeChr{nullptr}; // 0x2a4e
|
NimBLECharacteristic* m_protocolModeChr{nullptr}; // 0x2a4e
|
||||||
NimBLECharacteristic* m_batteryLevelChr{nullptr}; // 0x2a19
|
NimBLECharacteristic* m_batteryLevelChr{nullptr}; // 0x2a19
|
||||||
|
|
||||||
|
NimBLECharacteristic* locateReportCharacteristicByIdAndType(uint8_t reportId, uint8_t reportType);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_BROADCASTER && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
|
#endif // CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_BROADCASTER) && MYNEWT_VAL(BLE_ROLE_PERIPHERAL)
|
||||||
#endif // NIMBLE_CPP_HIDDEVICE_H_
|
#endif // NIMBLE_CPP_HIDDEVICE_H_
|
||||||
|
|||||||
314
src/NimBLEL2CAPChannel.cpp
Normal file
314
src/NimBLEL2CAPChannel.cpp
Normal file
@@ -0,0 +1,314 @@
|
|||||||
|
//
|
||||||
|
// (C) Dr. Michael 'Mickey' Lauer <mickey@vanille-media.de>
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "NimBLEL2CAPChannel.h"
|
||||||
|
#if CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_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<uint8_t>::const_iterator begin, std::vector<uint8_t>::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 MYNEWT_VAL(BLE_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 // MYNEWT_VAL(BLE_ROLE_CENTRAL)
|
||||||
|
|
||||||
|
bool NimBLEL2CAPChannel::write(const std::vector<uint8_t>& 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<uint8_t> 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<NimBLEL2CAPChannel*>(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_NIMBLE_ENABLED && MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM)
|
||||||
126
src/NimBLEL2CAPChannel.h
Normal file
126
src/NimBLEL2CAPChannel.h
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
//
|
||||||
|
// (C) Dr. Michael 'Mickey' Lauer <mickey@vanille-media.de>
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef NIMBLE_CPP_L2CAPCHANNEL_H_
|
||||||
|
#define NIMBLE_CPP_L2CAPCHANNEL_H_
|
||||||
|
|
||||||
|
#include "syscfg/syscfg.h"
|
||||||
|
#if CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_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 <vector>
|
||||||
|
# include <atomic>
|
||||||
|
|
||||||
|
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<uint8_t>& 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<bool> 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<uint8_t>::const_iterator begin, std::vector<uint8_t>::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<uint8_t>& data) {};
|
||||||
|
/// Called after the channel has been disconnected.
|
||||||
|
/// Default implementation does nothing.
|
||||||
|
virtual void onDisconnect(NimBLEL2CAPChannel* channel) {};
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM)
|
||||||
|
#endif // NIMBLE_CPP_L2CAPCHANNEL_H_
|
||||||
40
src/NimBLEL2CAPServer.cpp
Normal file
40
src/NimBLEL2CAPServer.cpp
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
//
|
||||||
|
// (C) Dr. Michael 'Mickey' Lauer <mickey@vanille-media.de>
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "NimBLEL2CAPServer.h"
|
||||||
|
#if CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_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_NIMBLE_ENABLED && MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM)
|
||||||
41
src/NimBLEL2CAPServer.h
Normal file
41
src/NimBLEL2CAPServer.h
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
//
|
||||||
|
// (C) Dr. Michael 'Mickey' Lauer <mickey@vanille-media.de>
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef NIMBLE_CPP_L2CAPSERVER_H_
|
||||||
|
#define NIMBLE_CPP_L2CAPSERVER_H_
|
||||||
|
#include "syscfg/syscfg.h"
|
||||||
|
#if CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM)
|
||||||
|
|
||||||
|
# include "inttypes.h"
|
||||||
|
# include <vector>
|
||||||
|
|
||||||
|
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<NimBLEL2CAPChannel*> services;
|
||||||
|
|
||||||
|
friend class NimBLEL2CAPChannel;
|
||||||
|
friend class NimBLEDevice;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM)
|
||||||
|
#endif // NIMBLE_CPP_L2CAPSERVER_H_
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
|
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
|
||||||
* esp-nimble-cpp, NimBLE-Arduino contributors.
|
* esp-nimble-cpp, NimBLE-Arduino contributors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@@ -18,8 +18,8 @@
|
|||||||
#ifndef NIMBLE_CPP_LOCAL_ATTRIBUTE_H_
|
#ifndef NIMBLE_CPP_LOCAL_ATTRIBUTE_H_
|
||||||
#define NIMBLE_CPP_LOCAL_ATTRIBUTE_H_
|
#define NIMBLE_CPP_LOCAL_ATTRIBUTE_H_
|
||||||
|
|
||||||
#include "nimconfig.h"
|
#include "syscfg/syscfg.h"
|
||||||
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
|
#if CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_PERIPHERAL)
|
||||||
|
|
||||||
# include "NimBLEAttribute.h"
|
# include "NimBLEAttribute.h"
|
||||||
|
|
||||||
@@ -54,5 +54,5 @@ class NimBLELocalAttribute : public NimBLEAttribute {
|
|||||||
uint8_t m_removed{0};
|
uint8_t m_removed{0};
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL
|
#endif // CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_PERIPHERAL)
|
||||||
#endif // NIMBLE_CPP_LOCAL_ATTRIBUTE_H_
|
#endif // NIMBLE_CPP_LOCAL_ATTRIBUTE_H_
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
|
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
|
||||||
* esp-nimble-cpp, NimBLE-Arduino contributors.
|
* esp-nimble-cpp, NimBLE-Arduino contributors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@@ -18,8 +18,8 @@
|
|||||||
#ifndef NIMBLE_LOCAL_VALUE_ATTRIBUTE_H_
|
#ifndef NIMBLE_LOCAL_VALUE_ATTRIBUTE_H_
|
||||||
#define NIMBLE_LOCAL_VALUE_ATTRIBUTE_H_
|
#define NIMBLE_LOCAL_VALUE_ATTRIBUTE_H_
|
||||||
|
|
||||||
#include "nimconfig.h"
|
#include "syscfg/syscfg.h"
|
||||||
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
|
#if CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_PERIPHERAL)
|
||||||
|
|
||||||
# if defined(CONFIG_NIMBLE_CPP_IDF)
|
# if defined(CONFIG_NIMBLE_CPP_IDF)
|
||||||
# include "host/ble_hs.h"
|
# include "host/ble_hs.h"
|
||||||
@@ -48,30 +48,18 @@ typedef enum {
|
|||||||
} NIMBLE_PROPERTY;
|
} NIMBLE_PROPERTY;
|
||||||
|
|
||||||
# include "NimBLELocalAttribute.h"
|
# include "NimBLELocalAttribute.h"
|
||||||
|
# include "NimBLEValueAttribute.h"
|
||||||
# include "NimBLEAttValue.h"
|
# include "NimBLEAttValue.h"
|
||||||
# include <vector>
|
# include <vector>
|
||||||
class NimBLEConnInfo;
|
class NimBLEConnInfo;
|
||||||
|
|
||||||
class NimBLELocalValueAttribute : public NimBLELocalAttribute {
|
class NimBLELocalValueAttribute : public NimBLELocalAttribute, public NimBLEValueAttribute {
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* @brief Get the properties of the attribute.
|
* @brief Get the properties of the attribute.
|
||||||
*/
|
*/
|
||||||
uint16_t getProperties() const { return m_properties; }
|
uint16_t getProperties() const { return m_properties; }
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get the length of the attribute value.
|
|
||||||
* @return The length of the attribute value.
|
|
||||||
*/
|
|
||||||
size_t getLength() const { return m_value.size(); }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @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 = nullptr) const { return m_value; }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Set the value of the attribute value.
|
* @brief Set the value of the attribute value.
|
||||||
* @param [in] data The data to set the value to.
|
* @param [in] data The data to set the value to.
|
||||||
@@ -100,19 +88,6 @@ class NimBLELocalValueAttribute : public NimBLELocalAttribute {
|
|||||||
m_value.setValue<T>(val);
|
m_value.setValue<T>(val);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Template to convert the data to <type\>.
|
|
||||||
* @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 <tt>sizeof(<type\>)</tt>.
|
|
||||||
* @return The data converted to <type\> or NULL if skipSizeCheck is false and the data is less than <tt>sizeof(<type\>)</tt>.
|
|
||||||
* @details <b>Use:</b> <tt>getValue<type>(×tamp, skipSizeCheck);</tt>
|
|
||||||
*/
|
|
||||||
template <typename T>
|
|
||||||
T getValue(time_t* timestamp = nullptr, bool skipSizeCheck = false) const {
|
|
||||||
return m_value.getValue<T>(timestamp, skipSizeCheck);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
friend class NimBLEServer;
|
friend class NimBLEServer;
|
||||||
|
|
||||||
@@ -126,9 +101,8 @@ class NimBLELocalValueAttribute : public NimBLELocalAttribute {
|
|||||||
NimBLELocalValueAttribute(const NimBLEUUID& uuid,
|
NimBLELocalValueAttribute(const NimBLEUUID& uuid,
|
||||||
uint16_t handle,
|
uint16_t handle,
|
||||||
uint16_t maxLen,
|
uint16_t maxLen,
|
||||||
uint16_t initLen = CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH)
|
uint16_t initLen = MYNEWT_VAL(NIMBLE_CPP_ATT_VALUE_INIT_LENGTH))
|
||||||
: NimBLELocalAttribute(uuid, handle), m_value(initLen, maxLen) {}
|
: NimBLELocalAttribute(uuid, handle), NimBLEValueAttribute(maxLen, initLen) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Destroy the NimBLELocalValueAttribute object.
|
* @brief Destroy the NimBLELocalValueAttribute object.
|
||||||
*/
|
*/
|
||||||
@@ -163,9 +137,8 @@ class NimBLELocalValueAttribute : public NimBLELocalAttribute {
|
|||||||
*/
|
*/
|
||||||
void setProperties(uint16_t properties) { m_properties = properties; }
|
void setProperties(uint16_t properties) { m_properties = properties; }
|
||||||
|
|
||||||
NimBLEAttValue m_value{};
|
uint16_t m_properties{0};
|
||||||
uint16_t m_properties{0};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL
|
#endif // CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_PERIPHERAL)
|
||||||
#endif // NIMBLE_LOCAL_VALUE_ATTRIBUTE_H_
|
#endif // NIMBLE_LOCAL_VALUE_ATTRIBUTE_H_
|
||||||
|
|||||||
154
src/NimBLELog.h
154
src/NimBLELog.h
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
|
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
|
||||||
* esp-nimble-cpp, NimBLE-Arduino contributors.
|
* esp-nimble-cpp, NimBLE-Arduino contributors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@@ -18,61 +18,167 @@
|
|||||||
#ifndef NIMBLE_CPP_LOG_H_
|
#ifndef NIMBLE_CPP_LOG_H_
|
||||||
#define NIMBLE_CPP_LOG_H_
|
#define NIMBLE_CPP_LOG_H_
|
||||||
|
|
||||||
#include "nimconfig.h"
|
#include "syscfg/syscfg.h"
|
||||||
#if defined(CONFIG_BT_ENABLED)
|
#if CONFIG_BT_NIMBLE_ENABLED
|
||||||
|
|
||||||
|
# ifndef MYNEWT_VAL_NIMBLE_CPP_LOG_LEVEL
|
||||||
|
# ifndef CONFIG_NIMBLE_CPP_LOG_LEVEL
|
||||||
|
# if defined(ARDUINO_ARCH_ESP32) && defined(CORE_DEBUG_LEVEL)
|
||||||
|
# define MYNEWT_VAL_NIMBLE_CPP_LOG_LEVEL CORE_DEBUG_LEVEL
|
||||||
|
# else
|
||||||
|
# define MYNEWT_VAL_NIMBLE_CPP_LOG_LEVEL 0
|
||||||
|
# endif
|
||||||
|
# else
|
||||||
|
# define MYNEWT_VAL_NIMBLE_CPP_LOG_LEVEL CONFIG_NIMBLE_CPP_LOG_LEVEL
|
||||||
|
# endif
|
||||||
|
# endif
|
||||||
|
|
||||||
# if defined(CONFIG_NIMBLE_CPP_IDF)
|
# if defined(CONFIG_NIMBLE_CPP_IDF)
|
||||||
# include "esp_log.h"
|
# include "esp_log.h"
|
||||||
# include "console/console.h"
|
# include "console/console.h"
|
||||||
# ifndef CONFIG_NIMBLE_CPP_LOG_LEVEL
|
|
||||||
# define CONFIG_NIMBLE_CPP_LOG_LEVEL 0
|
|
||||||
# endif
|
|
||||||
|
|
||||||
# define NIMBLE_CPP_LOG_PRINT(level, tag, format, ...) \
|
# if defined(CONFIG_NIMBLE_CPP_LOG_OVERRIDE_COLOR)
|
||||||
do { \
|
# if CONFIG_LOG_COLORS
|
||||||
if (CONFIG_NIMBLE_CPP_LOG_LEVEL >= level) ESP_LOG_LEVEL_LOCAL(level, tag, format, ##__VA_ARGS__); \
|
# 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
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
|
# 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_CPP_LOG_FORMAT(letter, format) NIMBLE_CPP_LOG_COLOR_##letter #letter " (%lu) %s: " format LOG_RESET_COLOR "\n"
|
||||||
|
|
||||||
|
# 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 (MYNEWT_VAL(NIMBLE_CPP_LOG_LEVEL) >= level) NIMBLE_CPP_LOG_LEVEL_LOCAL(level, tag, format, ##__VA_ARGS__); \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
|
# else
|
||||||
|
# define NIMBLE_CPP_LOG_PRINT(level, tag, format, ...) \
|
||||||
|
do { \
|
||||||
|
if (MYNEWT_VAL(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_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_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_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__)
|
# define NIMBLE_LOGE(tag, format, ...) NIMBLE_CPP_LOG_PRINT(ESP_LOG_ERROR, tag, format, ##__VA_ARGS__)
|
||||||
|
|
||||||
# else
|
# else
|
||||||
# include "nimble/porting/nimble/include/syscfg/syscfg.h"
|
|
||||||
# include "nimble/console/console.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
|
|
||||||
# endif
|
|
||||||
|
|
||||||
# if CONFIG_NIMBLE_CPP_LOG_LEVEL >= 4
|
# if MYNEWT_VAL(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
|
# else
|
||||||
# define NIMBLE_LOGD(tag, format, ...) (void)tag
|
# define NIMBLE_LOGD(tag, format, ...) (void)tag
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
# if CONFIG_NIMBLE_CPP_LOG_LEVEL >= 3
|
# if MYNEWT_VAL(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
|
# else
|
||||||
# define NIMBLE_LOGI(tag, format, ...) (void)tag
|
# define NIMBLE_LOGI(tag, format, ...) (void)tag
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
# if CONFIG_NIMBLE_CPP_LOG_LEVEL >= 2
|
# if MYNEWT_VAL(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
|
# else
|
||||||
# define NIMBLE_LOGW(tag, format, ...) (void)tag
|
# define NIMBLE_LOGW(tag, format, ...) (void)tag
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
# if CONFIG_NIMBLE_CPP_LOG_LEVEL >= 1
|
# if MYNEWT_VAL(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
|
# else
|
||||||
# define NIMBLE_LOGE(tag, format, ...) (void)tag
|
# define NIMBLE_LOGE(tag, format, ...) (void)tag
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
# endif /* CONFIG_NIMBLE_CPP_IDF */
|
# endif /* CONFIG_NIMBLE_CPP_IDF */
|
||||||
#endif /* CONFIG_BT_ENABLED */
|
|
||||||
#endif /* NIMBLE_CPP_LOG_H_ */
|
# 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)); }}
|
||||||
|
|
||||||
|
#endif /* CONFIG_BT_NIMBLE_ENABLED */
|
||||||
|
#endif /* NIMBLE_CPP_LOG_H_ */
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
|
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
|
||||||
* esp-nimble-cpp, NimBLE-Arduino contributors.
|
* esp-nimble-cpp, NimBLE-Arduino contributors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@@ -15,10 +15,9 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "nimconfig.h"
|
#include "NimBLERemoteCharacteristic.h"
|
||||||
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
|
#if CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_CENTRAL)
|
||||||
|
|
||||||
# include "NimBLERemoteCharacteristic.h"
|
|
||||||
# include "NimBLERemoteDescriptor.h"
|
# include "NimBLERemoteDescriptor.h"
|
||||||
# include "NimBLERemoteService.h"
|
# include "NimBLERemoteService.h"
|
||||||
# include "NimBLEClient.h"
|
# include "NimBLEClient.h"
|
||||||
@@ -27,17 +26,18 @@
|
|||||||
|
|
||||||
# include <climits>
|
# include <climits>
|
||||||
|
|
||||||
typedef struct {
|
struct NimBLEDescriptorFilter {
|
||||||
const NimBLEUUID* uuid;
|
NimBLERemoteDescriptor* dsc;
|
||||||
void* task_data;
|
const NimBLEUUID* uuid;
|
||||||
} desc_filter_t;
|
void* taskData;
|
||||||
|
};
|
||||||
|
|
||||||
static const char* LOG_TAG = "NimBLERemoteCharacteristic";
|
static const char* LOG_TAG = "NimBLERemoteCharacteristic";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Constructor.
|
* @brief Constructor.
|
||||||
* @param [in] svc A pointer to the service this characteristic belongs to.
|
* @param [in] svc A pointer to the service this characteristic belongs to.
|
||||||
* @param [in] ble_gatt_chr struct defined as:
|
* @param [in] chr struct defined as:
|
||||||
* struct ble_gatt_chr {
|
* struct ble_gatt_chr {
|
||||||
* uint16_t def_handle;
|
* uint16_t def_handle;
|
||||||
* uint16_t val_handle;
|
* uint16_t val_handle;
|
||||||
@@ -63,71 +63,89 @@ NimBLERemoteCharacteristic::~NimBLERemoteCharacteristic() {
|
|||||||
* @brief Callback used by the API when a descriptor is discovered or search complete.
|
* @brief Callback used by the API when a descriptor is discovered or search complete.
|
||||||
*/
|
*/
|
||||||
int NimBLERemoteCharacteristic::descriptorDiscCB(
|
int NimBLERemoteCharacteristic::descriptorDiscCB(
|
||||||
uint16_t conn_handle, const ble_gatt_error* error, uint16_t chr_val_handle, const ble_gatt_dsc* dsc, void* arg) {
|
uint16_t connHandle, const ble_gatt_error* error, uint16_t chrHandle, const ble_gatt_dsc* dsc, void* arg) {
|
||||||
int rc = error->status;
|
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);
|
NIMBLE_LOGD(LOG_TAG, "Descriptor Discovery >> status: %d handle: %d", rc, (rc == 0) ? dsc->handle : -1);
|
||||||
|
|
||||||
auto filter = (desc_filter_t*)arg;
|
// Results for chrHandle added until rc != 0
|
||||||
auto pTaskData = (NimBLETaskData*)filter->task_data;
|
// Must find specified UUID if filter is used
|
||||||
const auto pChr = (NimBLERemoteCharacteristic*)pTaskData->m_pInstance;
|
if (rc == 0 && pChr->getHandle() == chrHandle && (!uuid || 0 == ble_uuid_cmp(uuid->getBase(), &dsc->uuid.u))) {
|
||||||
const NimBLEUUID* uuidFilter = filter->uuid;
|
// Return BLE_HS_EDONE if the descriptor was found, stop the search
|
||||||
|
|
||||||
if (error->status == BLE_HS_ENOTCONN) {
|
|
||||||
NIMBLE_LOGE(LOG_TAG, "<< Descriptor Discovery; Not connected");
|
|
||||||
NimBLEUtils::taskRelease(*pTaskData, error->status);
|
|
||||||
return error->status;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pChr->getHandle() != chr_val_handle) {
|
|
||||||
rc = BLE_HS_EDONE; // descriptor not for this characteristic
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rc == 0) {
|
|
||||||
if (uuidFilter != nullptr) {
|
|
||||||
if (ble_uuid_cmp(uuidFilter->getBase(), &dsc->uuid.u) == 0) {
|
|
||||||
rc = BLE_HS_EDONE; // Found the descriptor, stop the search
|
|
||||||
} else {
|
|
||||||
return 0; // Not the descriptor we are looking for
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pChr->m_vDescriptors.push_back(new NimBLERemoteDescriptor(pChr, dsc));
|
pChr->m_vDescriptors.push_back(new NimBLERemoteDescriptor(pChr, dsc));
|
||||||
|
rc = !!uuid * BLE_HS_EDONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
NimBLEUtils::taskRelease(*pTaskData, rc);
|
if (rc != 0) {
|
||||||
NIMBLE_LOGD(LOG_TAG, "<< Descriptor Discovery");
|
NimBLEUtils::taskRelease(*pTaskData, rc);
|
||||||
|
NIMBLE_LOGD(LOG_TAG, "<< Descriptor Discovery");
|
||||||
|
}
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Populate the descriptors (if any) for this characteristic.
|
* @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* uuidFilter) const {
|
bool NimBLERemoteCharacteristic::retrieveDescriptors(NimBLEDescriptorFilter* pFilter) const {
|
||||||
NIMBLE_LOGD(LOG_TAG, ">> retrieveDescriptors() for characteristic: %s", getUUID().toString().c_str());
|
NIMBLE_LOGD(LOG_TAG, ">> retrieveDescriptors() for characteristic: %s", getUUID().toString().c_str());
|
||||||
|
|
||||||
NimBLETaskData taskData(const_cast<NimBLERemoteCharacteristic*>(this));
|
const auto pSvc = getRemoteService();
|
||||||
desc_filter_t filter = {uuidFilter, &taskData};
|
uint16_t endHandle = pSvc->getEndHandle();
|
||||||
|
|
||||||
|
// Find the handle of the next characteristic to limit the descriptor search range.
|
||||||
|
const auto& chars = pSvc->getCharacteristics(false);
|
||||||
|
for (auto it = chars.begin(); it != chars.end(); ++it) {
|
||||||
|
if ((*it)->getHandle() == this->getHandle()) {
|
||||||
|
auto next_it = std::next(it);
|
||||||
|
if (next_it != chars.end()) {
|
||||||
|
endHandle = (*next_it)->getHandle() - 1;
|
||||||
|
NIMBLE_LOGD(LOG_TAG, "Search range limited to handle 0x%04X", endHandle);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If this is the last handle then there are no descriptors
|
||||||
|
if (getHandle() == endHandle) {
|
||||||
|
NIMBLE_LOGD(LOG_TAG, "<< retrieveDescriptors(): found 0 descriptors.");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
NimBLETaskData taskData(const_cast<NimBLERemoteCharacteristic*>(this));
|
||||||
|
NimBLEDescriptorFilter defaultFilter{nullptr, nullptr, &taskData};
|
||||||
|
if (pFilter == nullptr) {
|
||||||
|
pFilter = &defaultFilter;
|
||||||
|
}
|
||||||
|
|
||||||
int rc = ble_gattc_disc_all_dscs(getClient()->getConnHandle(),
|
int rc = ble_gattc_disc_all_dscs(getClient()->getConnHandle(),
|
||||||
getHandle(),
|
getHandle(),
|
||||||
getRemoteService()->getEndHandle(),
|
endHandle,
|
||||||
NimBLERemoteCharacteristic::descriptorDiscCB,
|
NimBLERemoteCharacteristic::descriptorDiscCB,
|
||||||
&filter);
|
pFilter);
|
||||||
if (rc != 0) {
|
if (rc != 0) {
|
||||||
NIMBLE_LOGE(LOG_TAG, "ble_gattc_disc_all_dscs: rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc));
|
NIMBLE_LOGE(LOG_TAG, "ble_gattc_disc_all_dscs: rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto prevDscCount = m_vDescriptors.size();
|
||||||
NimBLEUtils::taskWait(taskData, BLE_NPL_TIME_FOREVER);
|
NimBLEUtils::taskWait(taskData, BLE_NPL_TIME_FOREVER);
|
||||||
rc = taskData.m_flags;
|
rc = ((NimBLETaskData*)pFilter->taskData)->m_flags;
|
||||||
if (rc == 0 || rc == BLE_HS_EDONE) {
|
if (rc != BLE_HS_EDONE) {
|
||||||
NIMBLE_LOGD(LOG_TAG, "<< retrieveDescriptors(): found %d descriptors.", m_vDescriptors.size());
|
NIMBLE_LOGE(LOG_TAG, "<< retrieveDescriptors(): failed: rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc));
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
NIMBLE_LOGE(LOG_TAG, "<< retrieveDescriptors(): failed: rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc));
|
if (m_vDescriptors.size() > prevDscCount) {
|
||||||
return false;
|
pFilter->dsc = m_vDescriptors.back();
|
||||||
|
}
|
||||||
|
|
||||||
|
NIMBLE_LOGD(LOG_TAG, "<< retrieveDescriptors(): found %d descriptors.", m_vDescriptors.size() - prevDscCount);
|
||||||
|
return true;
|
||||||
} // retrieveDescriptors
|
} // retrieveDescriptors
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -137,51 +155,38 @@ bool NimBLERemoteCharacteristic::retrieveDescriptors(const NimBLEUUID* uuidFilte
|
|||||||
*/
|
*/
|
||||||
NimBLERemoteDescriptor* NimBLERemoteCharacteristic::getDescriptor(const NimBLEUUID& uuid) const {
|
NimBLERemoteDescriptor* NimBLERemoteCharacteristic::getDescriptor(const NimBLEUUID& uuid) const {
|
||||||
NIMBLE_LOGD(LOG_TAG, ">> getDescriptor: uuid: %s", uuid.toString().c_str());
|
NIMBLE_LOGD(LOG_TAG, ">> getDescriptor: uuid: %s", uuid.toString().c_str());
|
||||||
NimBLERemoteDescriptor* pDsc = nullptr;
|
NimBLEUUID uuidTmp{uuid};
|
||||||
size_t prev_size = m_vDescriptors.size();
|
NimBLETaskData taskData(const_cast<NimBLERemoteCharacteristic*>(this));
|
||||||
|
NimBLEDescriptorFilter filter{nullptr, &uuidTmp, &taskData};
|
||||||
|
|
||||||
for (const auto& it : m_vDescriptors) {
|
for (const auto& dsc : m_vDescriptors) {
|
||||||
if (it->getUUID() == uuid) {
|
if (dsc->getUUID() == uuid) {
|
||||||
pDsc = it;
|
filter.dsc = dsc;
|
||||||
goto Done;
|
goto Done;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (retrieveDescriptors(&uuid)) {
|
if (!retrieveDescriptors(&filter) || filter.dsc) {
|
||||||
if (m_vDescriptors.size() > prev_size) {
|
goto Done;
|
||||||
pDsc = m_vDescriptors.back();
|
}
|
||||||
goto Done;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the request was successful but 16/32 bit uuid not found
|
// Try again with 128 bit uuid if request succeeded but no descriptor found.
|
||||||
// try again with the 128 bit uuid.
|
if (uuid.bitSize() != BLE_UUID_TYPE_128) {
|
||||||
if (uuid.bitSize() == BLE_UUID_TYPE_16 || uuid.bitSize() == BLE_UUID_TYPE_32) {
|
uuidTmp.to128();
|
||||||
NimBLEUUID uuid128(uuid);
|
retrieveDescriptors(&filter);
|
||||||
uuid128.to128();
|
goto Done;
|
||||||
if (retrieveDescriptors(&uuid128)) {
|
}
|
||||||
if (m_vDescriptors.size() > prev_size) {
|
|
||||||
pDsc = m_vDescriptors.back();
|
// If the uuid was 128 bit, try again with 16 bit uuid.
|
||||||
}
|
uuidTmp.to16();
|
||||||
}
|
if (uuidTmp.bitSize() == BLE_UUID_TYPE_16) {
|
||||||
} else {
|
filter.uuid = &uuidTmp;
|
||||||
// If the request was successful but the 128 bit uuid not found
|
retrieveDescriptors(&filter);
|
||||||
// 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_vDescriptors.size() > prev_size) {
|
|
||||||
pDsc = m_vDescriptors.back();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Done:
|
Done:
|
||||||
NIMBLE_LOGD(LOG_TAG, "<< getDescriptor: %sfound", pDsc ? "" : "not ");
|
NIMBLE_LOGD(LOG_TAG, "<< getDescriptor: %sfound", filter.dsc ? "" : "not ");
|
||||||
return pDsc;
|
return filter.dsc;
|
||||||
} // getDescriptor
|
} // getDescriptor
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -310,7 +315,7 @@ size_t NimBLERemoteCharacteristic::deleteDescriptor(const NimBLEUUID& uuid) cons
|
|||||||
* @return True if supported.
|
* @return True if supported.
|
||||||
*/
|
*/
|
||||||
bool NimBLERemoteCharacteristic::canBroadcast() const {
|
bool NimBLERemoteCharacteristic::canBroadcast() const {
|
||||||
return (m_properties & BLE_GATT_CHR_PROP_BROADCAST) != 0;
|
return (m_properties & BLE_GATT_CHR_PROP_BROADCAST);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -398,4 +403,4 @@ NimBLEClient* NimBLERemoteCharacteristic::getClient() const {
|
|||||||
return getRemoteService()->getClient();
|
return getRemoteService()->getClient();
|
||||||
} // getClient
|
} // getClient
|
||||||
|
|
||||||
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL */
|
#endif // CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_CENTRAL)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
|
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
|
||||||
* esp-nimble-cpp, NimBLE-Arduino contributors.
|
* esp-nimble-cpp, NimBLE-Arduino contributors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@@ -18,15 +18,17 @@
|
|||||||
#ifndef NIMBLE_CPP_REMOTE_CHARACTERISTIC_H_
|
#ifndef NIMBLE_CPP_REMOTE_CHARACTERISTIC_H_
|
||||||
#define NIMBLE_CPP_REMOTE_CHARACTERISTIC_H_
|
#define NIMBLE_CPP_REMOTE_CHARACTERISTIC_H_
|
||||||
|
|
||||||
#include "nimconfig.h"
|
#include "syscfg/syscfg.h"
|
||||||
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
|
#if CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_CENTRAL)
|
||||||
|
|
||||||
# include "NimBLERemoteValueAttribute.h"
|
# include "NimBLERemoteValueAttribute.h"
|
||||||
# include <vector>
|
# include <vector>
|
||||||
# include <functional>
|
# include <functional>
|
||||||
|
|
||||||
|
class NimBLEUUID;
|
||||||
class NimBLERemoteService;
|
class NimBLERemoteService;
|
||||||
class NimBLERemoteDescriptor;
|
class NimBLERemoteDescriptor;
|
||||||
|
struct NimBLEDescriptorFilter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief A model of a remote BLE characteristic.
|
* @brief A model of a remote BLE characteristic.
|
||||||
@@ -65,10 +67,10 @@ class NimBLERemoteCharacteristic : public NimBLERemoteValueAttribute {
|
|||||||
~NimBLERemoteCharacteristic();
|
~NimBLERemoteCharacteristic();
|
||||||
|
|
||||||
bool setNotify(uint16_t val, notify_callback notifyCallback = nullptr, bool response = true) const;
|
bool setNotify(uint16_t val, notify_callback notifyCallback = nullptr, bool response = true) const;
|
||||||
bool retrieveDescriptors(const NimBLEUUID* uuidFilter = nullptr) const;
|
bool retrieveDescriptors(NimBLEDescriptorFilter* pFilter = nullptr) const;
|
||||||
|
|
||||||
static int descriptorDiscCB(
|
static int descriptorDiscCB(
|
||||||
uint16_t conn_handle, const ble_gatt_error* error, uint16_t chr_val_handle, const ble_gatt_dsc* dsc, void* arg);
|
uint16_t connHandle, const ble_gatt_error* error, uint16_t chrHandle, const ble_gatt_dsc* dsc, void* arg);
|
||||||
|
|
||||||
const NimBLERemoteService* m_pRemoteService{nullptr};
|
const NimBLERemoteService* m_pRemoteService{nullptr};
|
||||||
uint8_t m_properties{0};
|
uint8_t m_properties{0};
|
||||||
@@ -77,5 +79,5 @@ class NimBLERemoteCharacteristic : public NimBLERemoteValueAttribute {
|
|||||||
|
|
||||||
}; // NimBLERemoteCharacteristic
|
}; // NimBLERemoteCharacteristic
|
||||||
|
|
||||||
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL */
|
#endif /* CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_CENTRAL) */
|
||||||
#endif /* NIMBLE_CPP_REMOTE_CHARACTERISTIC_H_ */
|
#endif /* NIMBLE_CPP_REMOTE_CHARACTERISTIC_H_ */
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
|
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
|
||||||
* esp-nimble-cpp, NimBLE-Arduino contributors.
|
* esp-nimble-cpp, NimBLE-Arduino contributors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@@ -15,10 +15,9 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "nimconfig.h"
|
#include "NimBLERemoteDescriptor.h"
|
||||||
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
|
#if CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_CENTRAL)
|
||||||
|
|
||||||
# include "NimBLERemoteDescriptor.h"
|
|
||||||
# include "NimBLERemoteCharacteristic.h"
|
# include "NimBLERemoteCharacteristic.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -57,4 +56,4 @@ NimBLEClient* NimBLERemoteDescriptor::getClient() const {
|
|||||||
return m_pRemoteCharacteristic->getClient();
|
return m_pRemoteCharacteristic->getClient();
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL */
|
#endif // CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_CENTRAL)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
|
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
|
||||||
* esp-nimble-cpp, NimBLE-Arduino contributors.
|
* esp-nimble-cpp, NimBLE-Arduino contributors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@@ -18,8 +18,8 @@
|
|||||||
#ifndef NIMBLE_CPP_REMOTE_DESCRIPTOR_H_
|
#ifndef NIMBLE_CPP_REMOTE_DESCRIPTOR_H_
|
||||||
#define NIMBLE_CPP_REMOTE_DESCRIPTOR_H_
|
#define NIMBLE_CPP_REMOTE_DESCRIPTOR_H_
|
||||||
|
|
||||||
#include "nimconfig.h"
|
#include "syscfg/syscfg.h"
|
||||||
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
|
#if CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_CENTRAL)
|
||||||
|
|
||||||
# include "NimBLERemoteValueAttribute.h"
|
# include "NimBLERemoteValueAttribute.h"
|
||||||
|
|
||||||
@@ -44,5 +44,5 @@ class NimBLERemoteDescriptor : public NimBLERemoteValueAttribute {
|
|||||||
const NimBLERemoteCharacteristic* m_pRemoteCharacteristic;
|
const NimBLERemoteCharacteristic* m_pRemoteCharacteristic;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL */
|
#endif // CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_CENTRAL)
|
||||||
#endif /* NIMBLE_CPP_REMOTE_DESCRIPTOR_H_ */
|
#endif // NIMBLE_CPP_REMOTE_DESCRIPTOR_H_
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
|
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
|
||||||
* esp-nimble-cpp, NimBLE-Arduino contributors.
|
* esp-nimble-cpp, NimBLE-Arduino contributors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@@ -15,10 +15,9 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "nimconfig.h"
|
#include "NimBLERemoteService.h"
|
||||||
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
|
#if CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_CENTRAL)
|
||||||
|
|
||||||
# include "NimBLERemoteService.h"
|
|
||||||
# include "NimBLERemoteCharacteristic.h"
|
# include "NimBLERemoteCharacteristic.h"
|
||||||
# include "NimBLEClient.h"
|
# include "NimBLEClient.h"
|
||||||
# include "NimBLEAttValue.h"
|
# include "NimBLEAttValue.h"
|
||||||
@@ -147,7 +146,10 @@ int NimBLERemoteService::characteristicDiscCB(uint16_t conn_handle,
|
|||||||
const ble_gatt_error* error,
|
const ble_gatt_error* error,
|
||||||
const ble_gatt_chr* chr,
|
const ble_gatt_chr* chr,
|
||||||
void* arg) {
|
void* arg) {
|
||||||
NIMBLE_LOGD(LOG_TAG, "Characteristic Discovery >>");
|
NIMBLE_LOGD(LOG_TAG,
|
||||||
|
"Characteristic Discovery >> status: %d handle: %d",
|
||||||
|
error->status,
|
||||||
|
(error->status == 0) ? chr->def_handle : -1);
|
||||||
auto pTaskData = (NimBLETaskData*)arg;
|
auto pTaskData = (NimBLETaskData*)arg;
|
||||||
const auto pSvc = (NimBLERemoteService*)pTaskData->m_pInstance;
|
const auto pSvc = (NimBLERemoteService*)pTaskData->m_pInstance;
|
||||||
|
|
||||||
@@ -300,4 +302,4 @@ std::string NimBLERemoteService::toString() const {
|
|||||||
return res;
|
return res;
|
||||||
} // toString
|
} // toString
|
||||||
|
|
||||||
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL */
|
#endif // CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_CENTRAL)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
|
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
|
||||||
* esp-nimble-cpp, NimBLE-Arduino contributors.
|
* esp-nimble-cpp, NimBLE-Arduino contributors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@@ -18,8 +18,8 @@
|
|||||||
#ifndef NIMBLE_CPP_REMOTE_SERVICE_H_
|
#ifndef NIMBLE_CPP_REMOTE_SERVICE_H_
|
||||||
#define NIMBLE_CPP_REMOTE_SERVICE_H_
|
#define NIMBLE_CPP_REMOTE_SERVICE_H_
|
||||||
|
|
||||||
#include "nimconfig.h"
|
#include "syscfg/syscfg.h"
|
||||||
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
|
#if CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_CENTRAL)
|
||||||
|
|
||||||
# include "NimBLEAttribute.h"
|
# include "NimBLEAttribute.h"
|
||||||
# include <vector>
|
# include <vector>
|
||||||
@@ -64,5 +64,5 @@ class NimBLERemoteService : public NimBLEAttribute {
|
|||||||
uint16_t m_endHandle{0};
|
uint16_t m_endHandle{0};
|
||||||
}; // NimBLERemoteService
|
}; // NimBLERemoteService
|
||||||
|
|
||||||
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL */
|
#endif // CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_CENTRAL)
|
||||||
#endif /* NIMBLE_CPP_REMOTE_SERVICE_H_*/
|
#endif // NIMBLE_CPP_REMOTE_SERVICE_H_
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
|
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
|
||||||
* esp-nimble-cpp, NimBLE-Arduino contributors.
|
* esp-nimble-cpp, NimBLE-Arduino contributors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@@ -15,12 +15,12 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "nimconfig.h"
|
#include "NimBLERemoteValueAttribute.h"
|
||||||
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
|
#if CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_CENTRAL)
|
||||||
|
|
||||||
# include "NimBLERemoteValueAttribute.h"
|
|
||||||
# include "NimBLEClient.h"
|
# include "NimBLEClient.h"
|
||||||
# include "NimBLEUtils.h"
|
# include "NimBLEUtils.h"
|
||||||
|
# include "NimBLELog.h"
|
||||||
|
|
||||||
# include <climits>
|
# include <climits>
|
||||||
|
|
||||||
@@ -121,7 +121,7 @@ int NimBLERemoteValueAttribute::onWriteCB(uint16_t conn_handle, const ble_gatt_e
|
|||||||
* @param [in] timestamp A pointer to a time_t struct to store the time the value was read.
|
* @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.
|
* @return The value of the remote characteristic.
|
||||||
*/
|
*/
|
||||||
NimBLEAttValue NimBLERemoteValueAttribute::readValue(time_t* timestamp) const {
|
NimBLEAttValue NimBLERemoteValueAttribute::readValue(time_t* timestamp) {
|
||||||
NIMBLE_LOGD(LOG_TAG, ">> readValue()");
|
NIMBLE_LOGD(LOG_TAG, ">> readValue()");
|
||||||
|
|
||||||
NimBLEAttValue value{};
|
NimBLEAttValue value{};
|
||||||
@@ -217,4 +217,4 @@ int NimBLERemoteValueAttribute::onReadCB(uint16_t conn_handle, const ble_gatt_er
|
|||||||
return rc;
|
return rc;
|
||||||
} // onReadCB
|
} // onReadCB
|
||||||
|
|
||||||
#endif // CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL
|
#endif // CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_CENTRAL)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
|
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
|
||||||
* esp-nimble-cpp, NimBLE-Arduino contributors.
|
* esp-nimble-cpp, NimBLE-Arduino contributors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@@ -18,8 +18,8 @@
|
|||||||
#ifndef NIMBLE_CPP_REMOTE_VALUE_ATTRIBUTE_H_
|
#ifndef NIMBLE_CPP_REMOTE_VALUE_ATTRIBUTE_H_
|
||||||
#define NIMBLE_CPP_REMOTE_VALUE_ATTRIBUTE_H_
|
#define NIMBLE_CPP_REMOTE_VALUE_ATTRIBUTE_H_
|
||||||
|
|
||||||
#include "nimconfig.h"
|
#include "syscfg/syscfg.h"
|
||||||
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
|
#if CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_CENTRAL)
|
||||||
|
|
||||||
# if defined(CONFIG_NIMBLE_CPP_IDF)
|
# if defined(CONFIG_NIMBLE_CPP_IDF)
|
||||||
# include <host/ble_gatt.h>
|
# include <host/ble_gatt.h>
|
||||||
@@ -32,32 +32,19 @@
|
|||||||
# undef max
|
# undef max
|
||||||
/**************************/
|
/**************************/
|
||||||
|
|
||||||
# include "NimBLEAttribute.h"
|
# include "NimBLEValueAttribute.h"
|
||||||
# include "NimBLEAttValue.h"
|
# include "NimBLEAttValue.h"
|
||||||
|
|
||||||
class NimBLEClient;
|
class NimBLEClient;
|
||||||
|
|
||||||
class NimBLERemoteValueAttribute : public NimBLEAttribute {
|
class NimBLERemoteValueAttribute : public NimBLEValueAttribute, public NimBLEAttribute {
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* @brief Read the value of the remote attribute.
|
* @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.
|
* @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.
|
* @return The value of the remote attribute.
|
||||||
*/
|
*/
|
||||||
NimBLEAttValue readValue(time_t* timestamp = nullptr) const;
|
NimBLEAttValue readValue(time_t* timestamp = nullptr);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get the length of the remote attribute value.
|
|
||||||
* @return The length of the remote attribute value.
|
|
||||||
*/
|
|
||||||
size_t getLength() const { return m_value.size(); }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get the value of the remote attribute.
|
|
||||||
* @return The value of the remote attribute.
|
|
||||||
* @details This returns a copy of the value to avoid potential race conditions.
|
|
||||||
*/
|
|
||||||
NimBLEAttValue getValue() const { return m_value; }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the client instance that owns this attribute.
|
* Get the client instance that owns this attribute.
|
||||||
@@ -122,13 +109,34 @@ class NimBLERemoteValueAttribute : public NimBLEAttribute {
|
|||||||
* @brief Template to set the remote characteristic value to <type\>val.
|
* @brief Template to set the remote characteristic value to <type\>val.
|
||||||
* @param [in] v The value to write.
|
* @param [in] v The value to write.
|
||||||
* @param [in] response True == request write response.
|
* @param [in] response True == request write response.
|
||||||
* @details Only used if the <type\> has a `data()` and `size()` method.
|
* @details Only used if the <type\> has a `data()` and `size()` method with `value_type`.
|
||||||
|
* Correctly calculates byte size for containers with multi-byte element types.
|
||||||
*/
|
*/
|
||||||
template <typename T>
|
template <typename T>
|
||||||
# ifdef _DOXYGEN_
|
# ifdef _DOXYGEN_
|
||||||
bool
|
bool
|
||||||
# else
|
# else
|
||||||
typename std::enable_if<Has_data_size<T>::value, bool>::type
|
typename std::enable_if<Has_data_size<T>::value && Has_value_type<T>::value, bool>::type
|
||||||
|
# endif
|
||||||
|
writeValue(const T& v, bool response = false) const {
|
||||||
|
return writeValue(
|
||||||
|
reinterpret_cast<const uint8_t*>(v.data()),
|
||||||
|
v.size() * sizeof(typename T::value_type),
|
||||||
|
response
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Template to set the remote characteristic value to <type\>val.
|
||||||
|
* @param [in] v The value to write.
|
||||||
|
* @param [in] response True == request write response.
|
||||||
|
* @details Only used if the <type\> has a `data()` and `size()` method without `value_type`.
|
||||||
|
*/
|
||||||
|
template <typename T>
|
||||||
|
# ifdef _DOXYGEN_
|
||||||
|
bool
|
||||||
|
# else
|
||||||
|
typename std::enable_if<Has_data_size<T>::value && !Has_value_type<T>::value, bool>::type
|
||||||
# endif
|
# endif
|
||||||
writeValue(const T& v, bool response = false) const {
|
writeValue(const T& v, bool response = false) const {
|
||||||
return writeValue(reinterpret_cast<const uint8_t*>(v.data()), v.size(), response);
|
return writeValue(reinterpret_cast<const uint8_t*>(v.data()), v.size(), response);
|
||||||
@@ -144,7 +152,11 @@ class NimBLERemoteValueAttribute : public NimBLEAttribute {
|
|||||||
template <typename T>
|
template <typename T>
|
||||||
typename std::enable_if<!std::is_pointer<T>::value, bool>::type writeValue(const T& v, bool response = false) const {
|
typename std::enable_if<!std::is_pointer<T>::value, bool>::type writeValue(const T& v, bool response = false) const {
|
||||||
if constexpr (Has_data_size<T>::value) {
|
if constexpr (Has_data_size<T>::value) {
|
||||||
return writeValue(reinterpret_cast<const uint8_t*>(v.data()), v.size(), response);
|
if constexpr (Has_value_type<T>::value) {
|
||||||
|
return writeValue(reinterpret_cast<const uint8_t*>(v.data()), v.size() * sizeof(typename T::value_type), response);
|
||||||
|
} else {
|
||||||
|
return writeValue(reinterpret_cast<const uint8_t*>(v.data()), v.size(), response);
|
||||||
|
}
|
||||||
} else if constexpr (Has_c_str_length<T>::value) {
|
} else if constexpr (Has_c_str_length<T>::value) {
|
||||||
return writeValue(reinterpret_cast<const uint8_t*>(v.c_str()), v.length(), response);
|
return writeValue(reinterpret_cast<const uint8_t*>(v.c_str()), v.length(), response);
|
||||||
} else {
|
} else {
|
||||||
@@ -153,20 +165,6 @@ class NimBLERemoteValueAttribute : public NimBLEAttribute {
|
|||||||
}
|
}
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Template to convert the remote characteristic data to <type\>.
|
|
||||||
* @tparam T The type to convert the data to.
|
|
||||||
* @param [in] timestamp A pointer to a time_t struct to store the time the value was read.
|
|
||||||
* @param [in] skipSizeCheck If true it will skip checking if the data size is less than <tt>sizeof(<type\>)</tt>.
|
|
||||||
* @return The data converted to <type\> or NULL if skipSizeCheck is false and the data is
|
|
||||||
* less than <tt>sizeof(<type\>)</tt>.
|
|
||||||
* @details <b>Use:</b> <tt>getValue<type>(×tamp, skipSizeCheck);</tt>
|
|
||||||
*/
|
|
||||||
template <typename T>
|
|
||||||
T getValue(time_t* timestamp = nullptr, bool skipSizeCheck = false) const {
|
|
||||||
return m_value.getValue<T>(timestamp, skipSizeCheck);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Template to convert the remote characteristic data to <type\>.
|
* @brief Template to convert the remote characteristic data to <type\>.
|
||||||
* @tparam T The type to convert the data to.
|
* @tparam T The type to convert the data to.
|
||||||
@@ -177,16 +175,16 @@ class NimBLERemoteValueAttribute : public NimBLEAttribute {
|
|||||||
* @details <b>Use:</b> <tt>readValue<type>(×tamp, skipSizeCheck);</tt>
|
* @details <b>Use:</b> <tt>readValue<type>(×tamp, skipSizeCheck);</tt>
|
||||||
*/
|
*/
|
||||||
template <typename T>
|
template <typename T>
|
||||||
T readValue(time_t* timestamp = nullptr, bool skipSizeCheck = false) const {
|
T readValue(time_t* timestamp = nullptr, bool skipSizeCheck = false) {
|
||||||
readValue();
|
readValue();
|
||||||
return m_value.getValue<T>(timestamp, skipSizeCheck);
|
return getValue<T>(timestamp, skipSizeCheck);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/**
|
/**
|
||||||
* @brief Construct a new NimBLERemoteValueAttribute object.
|
* @brief Construct a new NimBLERemoteValueAttribute object.
|
||||||
*/
|
*/
|
||||||
NimBLERemoteValueAttribute(const ble_uuid_any_t& uuid, uint16_t handle) : NimBLEAttribute(uuid, handle) {}
|
NimBLERemoteValueAttribute(const ble_uuid_any_t& uuid, uint16_t handle) : NimBLEAttribute{uuid, handle} {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Destroy the NimBLERemoteValueAttribute object.
|
* @brief Destroy the NimBLERemoteValueAttribute object.
|
||||||
@@ -195,9 +193,7 @@ class NimBLERemoteValueAttribute : public NimBLEAttribute {
|
|||||||
|
|
||||||
static int onReadCB(uint16_t conn_handle, const ble_gatt_error* error, ble_gatt_attr* attr, void* arg);
|
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);
|
static int onWriteCB(uint16_t conn_handle, const ble_gatt_error* error, ble_gatt_attr* attr, void* arg);
|
||||||
|
|
||||||
mutable NimBLEAttValue m_value{};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL */
|
#endif // CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_CENTRAL)
|
||||||
#endif // NIMBLE_CPP_REMOTE_VALUE_ATTRIBUTE_H_
|
#endif // NIMBLE_CPP_REMOTE_VALUE_ATTRIBUTE_H_
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
|
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
|
||||||
* esp-nimble-cpp, NimBLE-Arduino contributors.
|
* esp-nimble-cpp, NimBLE-Arduino contributors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@@ -15,10 +15,9 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "nimconfig.h"
|
#include "NimBLEScan.h"
|
||||||
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
|
#if CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_OBSERVER)
|
||||||
|
|
||||||
# include "NimBLEScan.h"
|
|
||||||
# include "NimBLEDevice.h"
|
# include "NimBLEDevice.h"
|
||||||
# include "NimBLELog.h"
|
# include "NimBLELog.h"
|
||||||
|
|
||||||
@@ -33,16 +32,30 @@ static NimBLEScanCallbacks defaultScanCallbacks;
|
|||||||
*/
|
*/
|
||||||
NimBLEScan::NimBLEScan()
|
NimBLEScan::NimBLEScan()
|
||||||
: m_pScanCallbacks{&defaultScanCallbacks},
|
: m_pScanCallbacks{&defaultScanCallbacks},
|
||||||
// default interval + window, no whitelist scan filter,not limited scan, no scan response, filter_duplicates
|
m_scanParams{
|
||||||
m_scanParams{0, 0, BLE_HCI_SCAN_FILT_NO_WL, 0, 1, 1},
|
.itvl = 0, // default interval
|
||||||
|
.window = 0, // default window
|
||||||
|
.filter_policy = BLE_HCI_SCAN_FILT_NO_WL, // no whitelist scan filter
|
||||||
|
.limited = 0, // no limited scan
|
||||||
|
.passive = 1, // no scan response
|
||||||
|
.filter_duplicates = 1, // filter duplicates
|
||||||
|
# if defined(ESP_PLATFORM) && !defined(CONFIG_USING_NIMBLE_COMPONENT)
|
||||||
|
# if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 2)
|
||||||
|
.disable_observer_mode = 0, // observer role enabled
|
||||||
|
# endif
|
||||||
|
# endif
|
||||||
|
},
|
||||||
m_pTaskData{nullptr},
|
m_pTaskData{nullptr},
|
||||||
m_maxResults{0xFF} {}
|
m_maxResults{0xFF} {
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Scan destructor, release any allocated resources.
|
* @brief Scan destructor, release any allocated resources.
|
||||||
*/
|
*/
|
||||||
NimBLEScan::~NimBLEScan() {
|
NimBLEScan::~NimBLEScan() {
|
||||||
clearResults();
|
for (const auto& dev : m_scanResults.m_deviceVec) {
|
||||||
|
delete dev;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -57,7 +70,12 @@ int NimBLEScan::handleGapEvent(ble_gap_event* event, void* arg) {
|
|||||||
switch (event->type) {
|
switch (event->type) {
|
||||||
case BLE_GAP_EVENT_EXT_DISC:
|
case BLE_GAP_EVENT_EXT_DISC:
|
||||||
case BLE_GAP_EVENT_DISC: {
|
case BLE_GAP_EVENT_DISC: {
|
||||||
# if CONFIG_BT_NIMBLE_EXT_ADV
|
if (!pScan->isScanning()) {
|
||||||
|
NIMBLE_LOGI(LOG_TAG, "Scan stopped, ignoring event");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
# if MYNEWT_VAL(BLE_EXT_ADV)
|
||||||
const auto& disc = event->ext_disc;
|
const auto& disc = event->ext_disc;
|
||||||
const bool isLegacyAdv = disc.props & BLE_HCI_ADV_LEGACY_MASK;
|
const bool isLegacyAdv = disc.props & BLE_HCI_ADV_LEGACY_MASK;
|
||||||
const auto event_type = isLegacyAdv ? disc.legacy_event_type : disc.props;
|
const auto event_type = isLegacyAdv ? disc.legacy_event_type : disc.props;
|
||||||
@@ -68,7 +86,7 @@ int NimBLEScan::handleGapEvent(ble_gap_event* event, void* arg) {
|
|||||||
# endif
|
# endif
|
||||||
NimBLEAddress advertisedAddress(disc.addr);
|
NimBLEAddress advertisedAddress(disc.addr);
|
||||||
|
|
||||||
# ifdef CONFIG_BT_NIMBLE_ROLE_CENTRAL
|
# if MYNEWT_VAL(BLE_ROLE_CENTRAL)
|
||||||
// stop processing if already connected
|
// stop processing if already connected
|
||||||
NimBLEClient* pClient = NimBLEDevice::getClientByPeerAddress(advertisedAddress);
|
NimBLEClient* pClient = NimBLEDevice::getClientByPeerAddress(advertisedAddress);
|
||||||
if (pClient != nullptr && pClient->isConnected()) {
|
if (pClient != nullptr && pClient->isConnected()) {
|
||||||
@@ -80,7 +98,7 @@ int NimBLEScan::handleGapEvent(ble_gap_event* event, void* arg) {
|
|||||||
|
|
||||||
// If we've seen this device before get a pointer to it from the vector
|
// If we've seen this device before get a pointer to it from the vector
|
||||||
for (const auto& dev : pScan->m_scanResults.m_deviceVec) {
|
for (const auto& dev : pScan->m_scanResults.m_deviceVec) {
|
||||||
# if CONFIG_BT_NIMBLE_EXT_ADV
|
# if MYNEWT_VAL(BLE_EXT_ADV)
|
||||||
// Same address but different set ID should create a new advertised device.
|
// Same address but different set ID should create a new advertised device.
|
||||||
if (dev->getAddress() == advertisedAddress && dev->getSetId() == disc.sid)
|
if (dev->getAddress() == advertisedAddress && dev->getSetId() == disc.sid)
|
||||||
# else
|
# else
|
||||||
@@ -111,27 +129,36 @@ int NimBLEScan::handleGapEvent(ble_gap_event* event, void* arg) {
|
|||||||
NIMBLE_LOGI(LOG_TAG, "New advertiser: %s", advertisedAddress.toString().c_str());
|
NIMBLE_LOGI(LOG_TAG, "New advertiser: %s", advertisedAddress.toString().c_str());
|
||||||
} else {
|
} else {
|
||||||
advertisedDevice->update(event, event_type);
|
advertisedDevice->update(event, event_type);
|
||||||
if (isLegacyAdv && event_type == BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP) {
|
if (isLegacyAdv) {
|
||||||
NIMBLE_LOGI(LOG_TAG, "Scan response from: %s", advertisedAddress.toString().c_str());
|
if (event_type == BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP) {
|
||||||
} else {
|
NIMBLE_LOGI(LOG_TAG, "Scan response from: %s", advertisedAddress.toString().c_str());
|
||||||
NIMBLE_LOGI(LOG_TAG, "Duplicate; updated: %s", advertisedAddress.toString().c_str());
|
} else {
|
||||||
|
NIMBLE_LOGI(LOG_TAG, "Duplicate; updated: %s", advertisedAddress.toString().c_str());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# if MYNEWT_VAL(BLE_EXT_ADV)
|
||||||
|
if (advertisedDevice->getDataStatus() == BLE_GAP_EXT_ADV_DATA_STATUS_INCOMPLETE) {
|
||||||
|
NIMBLE_LOGD(LOG_TAG, "EXT ADV data incomplete, waiting for more");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
# endif
|
||||||
|
|
||||||
if (!advertisedDevice->m_callbackSent) {
|
if (!advertisedDevice->m_callbackSent) {
|
||||||
pScan->m_pScanCallbacks->onDiscovered(advertisedDevice);
|
|
||||||
advertisedDevice->m_callbackSent++;
|
advertisedDevice->m_callbackSent++;
|
||||||
|
pScan->m_pScanCallbacks->onDiscovered(advertisedDevice);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If not active scanning or scan response is not available
|
// If not active scanning or scan response is not available
|
||||||
// or extended advertisement scanning, report the result to the callback now.
|
// or extended advertisement scanning, report the result to the callback now.
|
||||||
if (pScan->m_scanParams.passive || !isLegacyAdv || !advertisedDevice->isScannable()) {
|
if (pScan->m_scanParams.passive || !isLegacyAdv || !advertisedDevice->isScannable()) {
|
||||||
pScan->m_pScanCallbacks->onResult(advertisedDevice);
|
|
||||||
advertisedDevice->m_callbackSent++;
|
advertisedDevice->m_callbackSent++;
|
||||||
|
pScan->m_pScanCallbacks->onResult(advertisedDevice);
|
||||||
} else if (isLegacyAdv && event_type == BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP) {
|
} else if (isLegacyAdv && event_type == BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP) {
|
||||||
|
advertisedDevice->m_callbackSent++;
|
||||||
// got the scan response report the full data.
|
// got the scan response report the full data.
|
||||||
pScan->m_pScanCallbacks->onResult(advertisedDevice);
|
pScan->m_pScanCallbacks->onResult(advertisedDevice);
|
||||||
advertisedDevice->m_callbackSent++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If not storing results and we have invoked the callback, delete the device.
|
// If not storing results and we have invoked the callback, delete the device.
|
||||||
@@ -266,7 +293,7 @@ bool NimBLEScan::isScanning() {
|
|||||||
return ble_gap_disc_active();
|
return ble_gap_disc_active();
|
||||||
}
|
}
|
||||||
|
|
||||||
# if CONFIG_BT_NIMBLE_EXT_ADV
|
# if MYNEWT_VAL(BLE_EXT_ADV)
|
||||||
/**
|
/**
|
||||||
* @brief Set the PHYs to scan.
|
* @brief Set the PHYs to scan.
|
||||||
* @param [in] phyMask The PHYs to scan, a bit mask of:
|
* @param [in] phyMask The PHYs to scan, a bit mask of:
|
||||||
@@ -318,7 +345,7 @@ bool NimBLEScan::start(uint32_t duration, bool isContinue, bool restart) {
|
|||||||
|
|
||||||
// If scanning is already active, call the functions anyway as the parameters can be changed.
|
// If scanning is already active, call the functions anyway as the parameters can be changed.
|
||||||
|
|
||||||
# if CONFIG_BT_NIMBLE_EXT_ADV
|
# if MYNEWT_VAL(BLE_EXT_ADV)
|
||||||
ble_gap_ext_disc_params scan_params;
|
ble_gap_ext_disc_params scan_params;
|
||||||
scan_params.passive = m_scanParams.passive;
|
scan_params.passive = m_scanParams.passive;
|
||||||
scan_params.itvl = m_scanParams.itvl;
|
scan_params.itvl = m_scanParams.itvl;
|
||||||
@@ -468,19 +495,26 @@ NimBLEScanResults NimBLEScan::getResults() {
|
|||||||
* @brief Clear the stored results of the scan.
|
* @brief Clear the stored results of the scan.
|
||||||
*/
|
*/
|
||||||
void NimBLEScan::clearResults() {
|
void NimBLEScan::clearResults() {
|
||||||
for (const auto& dev : m_scanResults.m_deviceVec) {
|
if (m_scanResults.m_deviceVec.size()) {
|
||||||
delete dev;
|
std::vector<NimBLEAdvertisedDevice*> 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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
std::vector<NimBLEAdvertisedDevice*>().swap(m_scanResults.m_deviceVec);
|
|
||||||
} // clearResults
|
} // clearResults
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Dump the scan results to the log.
|
* @brief Dump the scan results to the log.
|
||||||
*/
|
*/
|
||||||
void NimBLEScanResults::dump() const {
|
void NimBLEScanResults::dump() const {
|
||||||
|
# if MYNEWT_VAL(NIMBLE_CPP_LOG_LEVEL) >= 3
|
||||||
for (const auto& dev : m_deviceVec) {
|
for (const auto& dev : m_deviceVec) {
|
||||||
NIMBLE_LOGI(LOG_TAG, "- %s", dev->toString().c_str());
|
NIMBLE_LOGI(LOG_TAG, "- %s", dev->toString().c_str());
|
||||||
}
|
}
|
||||||
|
# endif
|
||||||
} // dump
|
} // dump
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -547,4 +581,4 @@ void NimBLEScanCallbacks::onScanEnd(const NimBLEScanResults& results, int reason
|
|||||||
NIMBLE_LOGD(CB_TAG, "Scan ended; reason %d, num results: %d", reason, results.getCount());
|
NIMBLE_LOGD(CB_TAG, "Scan ended; reason %d, num results: %d", reason, results.getCount());
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_OBSERVER */
|
#endif // CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_OBSERVER)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
|
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
|
||||||
* esp-nimble-cpp, NimBLE-Arduino contributors.
|
* esp-nimble-cpp, NimBLE-Arduino contributors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@@ -18,8 +18,8 @@
|
|||||||
#ifndef NIMBLE_CPP_SCAN_H_
|
#ifndef NIMBLE_CPP_SCAN_H_
|
||||||
#define NIMBLE_CPP_SCAN_H_
|
#define NIMBLE_CPP_SCAN_H_
|
||||||
|
|
||||||
#include "nimconfig.h"
|
#include "syscfg/syscfg.h"
|
||||||
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
|
#if CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_OBSERVER)
|
||||||
|
|
||||||
# include "NimBLEAdvertisedDevice.h"
|
# include "NimBLEAdvertisedDevice.h"
|
||||||
# include "NimBLEUtils.h"
|
# include "NimBLEUtils.h"
|
||||||
@@ -83,7 +83,7 @@ class NimBLEScan {
|
|||||||
void erase(const NimBLEAddress& address);
|
void erase(const NimBLEAddress& address);
|
||||||
void erase(const NimBLEAdvertisedDevice* device);
|
void erase(const NimBLEAdvertisedDevice* device);
|
||||||
|
|
||||||
# if CONFIG_BT_NIMBLE_EXT_ADV
|
# if MYNEWT_VAL(BLE_EXT_ADV)
|
||||||
enum Phy { SCAN_1M = 0x01, SCAN_CODED = 0x02, SCAN_ALL = 0x03 };
|
enum Phy { SCAN_1M = 0x01, SCAN_CODED = 0x02, SCAN_ALL = 0x03 };
|
||||||
void setPhy(Phy phyMask);
|
void setPhy(Phy phyMask);
|
||||||
void setPeriod(uint32_t periodMs);
|
void setPeriod(uint32_t periodMs);
|
||||||
@@ -103,7 +103,7 @@ class NimBLEScan {
|
|||||||
NimBLETaskData* m_pTaskData;
|
NimBLETaskData* m_pTaskData;
|
||||||
uint8_t m_maxResults;
|
uint8_t m_maxResults;
|
||||||
|
|
||||||
# if CONFIG_BT_NIMBLE_EXT_ADV
|
# if MYNEWT_VAL(BLE_EXT_ADV)
|
||||||
uint8_t m_phy{SCAN_ALL};
|
uint8_t m_phy{SCAN_ALL};
|
||||||
uint16_t m_period{0};
|
uint16_t m_period{0};
|
||||||
# endif
|
# endif
|
||||||
@@ -136,5 +136,5 @@ class NimBLEScanCallbacks {
|
|||||||
virtual void onScanEnd(const NimBLEScanResults& scanResults, int reason);
|
virtual void onScanEnd(const NimBLEScanResults& scanResults, int reason);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // CONFIG_BT_ENABLED CONFIG_BT_NIMBLE_ROLE_OBSERVER
|
#endif // CONFIG_BT_NIMBLE_ENABLED MYNEWT_VAL(BLE_ROLE_OBSERVER)
|
||||||
#endif // NIMBLE_CPP_SCAN_H_
|
#endif // NIMBLE_CPP_SCAN_H_
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
|
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
|
||||||
* esp-nimble-cpp, NimBLE-Arduino contributors.
|
* esp-nimble-cpp, NimBLE-Arduino contributors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@@ -15,14 +15,13 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "nimconfig.h"
|
#include "NimBLEServer.h"
|
||||||
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
|
#if CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_PERIPHERAL)
|
||||||
|
|
||||||
# include "NimBLEServer.h"
|
|
||||||
# include "NimBLEDevice.h"
|
# include "NimBLEDevice.h"
|
||||||
# include "NimBLELog.h"
|
# include "NimBLELog.h"
|
||||||
|
|
||||||
# if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
|
# if MYNEWT_VAL(BLE_ROLE_CENTRAL)
|
||||||
# include "NimBLEClient.h"
|
# include "NimBLEClient.h"
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
@@ -40,6 +39,11 @@
|
|||||||
static const char* LOG_TAG = "NimBLEServer";
|
static const char* LOG_TAG = "NimBLEServer";
|
||||||
static NimBLEServerCallbacks defaultCallbacks;
|
static NimBLEServerCallbacks defaultCallbacks;
|
||||||
|
|
||||||
|
struct gattRegisterCallbackArgs {
|
||||||
|
NimBLEService* pSvc{nullptr};
|
||||||
|
NimBLECharacteristic* pChar{nullptr};
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Construct a BLE Server
|
* @brief Construct a BLE Server
|
||||||
*
|
*
|
||||||
@@ -50,7 +54,7 @@ NimBLEServer::NimBLEServer()
|
|||||||
: m_gattsStarted{false},
|
: m_gattsStarted{false},
|
||||||
m_svcChanged{false},
|
m_svcChanged{false},
|
||||||
m_deleteCallbacks{false},
|
m_deleteCallbacks{false},
|
||||||
# if !CONFIG_BT_NIMBLE_EXT_ADV
|
# if !MYNEWT_VAL(BLE_EXT_ADV)
|
||||||
m_advertiseOnDisconnect{false},
|
m_advertiseOnDisconnect{false},
|
||||||
# endif
|
# endif
|
||||||
m_pServerCallbacks{&defaultCallbacks},
|
m_pServerCallbacks{&defaultCallbacks},
|
||||||
@@ -70,9 +74,11 @@ NimBLEServer::~NimBLEServer() {
|
|||||||
delete m_pServerCallbacks;
|
delete m_pServerCallbacks;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# if MYNEWT_VAL(BLE_ROLE_CENTRAL)
|
||||||
if (m_pClient != nullptr) {
|
if (m_pClient != nullptr) {
|
||||||
delete m_pClient;
|
delete m_pClient;
|
||||||
}
|
}
|
||||||
|
# endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -142,17 +148,32 @@ NimBLEService* NimBLEServer::getServiceByHandle(uint16_t handle) const {
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
# if CONFIG_BT_NIMBLE_EXT_ADV
|
/**
|
||||||
|
* @brief Get a BLE Characteristic by its handle
|
||||||
|
* @param handle The handle of the characteristic.
|
||||||
|
* @return A pointer to the characteristic object or nullptr if not found.
|
||||||
|
*/
|
||||||
|
NimBLECharacteristic* NimBLEServer::getCharacteristicByHandle(uint16_t handle) const {
|
||||||
|
for (const auto& svc : m_svcVec) {
|
||||||
|
NimBLECharacteristic* pChr = svc->getCharacteristicByHandle(handle);
|
||||||
|
if (pChr != nullptr) {
|
||||||
|
return pChr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
} // getCharacteristicByHandle
|
||||||
|
|
||||||
|
# if MYNEWT_VAL(BLE_EXT_ADV)
|
||||||
/**
|
/**
|
||||||
* @brief Retrieve the advertising object that can be used to advertise the existence of the server.
|
* @brief Retrieve the advertising object that can be used to advertise the existence of the server.
|
||||||
* @return A pinter to an advertising object.
|
* @return A pointer to an advertising object.
|
||||||
*/
|
*/
|
||||||
NimBLEExtAdvertising* NimBLEServer::getAdvertising() const {
|
NimBLEExtAdvertising* NimBLEServer::getAdvertising() const {
|
||||||
return NimBLEDevice::getAdvertising();
|
return NimBLEDevice::getAdvertising();
|
||||||
} // getAdvertising
|
} // getAdvertising
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
# if !CONFIG_BT_NIMBLE_EXT_ADV || defined(_DOXYGEN_)
|
# if (!MYNEWT_VAL(BLE_EXT_ADV) && MYNEWT_VAL(BLE_ROLE_BROADCASTER)) || defined(_DOXYGEN_)
|
||||||
/**
|
/**
|
||||||
* @brief Retrieve the advertising object that can be used to advertise the existence of the server.
|
* @brief Retrieve the advertising object that can be used to advertise the existence of the server.
|
||||||
* @return A pointer to an advertising object.
|
* @return A pointer to an advertising object.
|
||||||
@@ -172,47 +193,122 @@ void NimBLEServer::serviceChanged() {
|
|||||||
}
|
}
|
||||||
} // serviceChanged
|
} // serviceChanged
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Callback for GATT registration events,
|
||||||
|
* used to obtain the assigned handles for services, characteristics, and descriptors.
|
||||||
|
* @param [in] ctxt The context of the registration event.
|
||||||
|
* @param [in] arg A pointer to the gattRegisterCallbackArgs struct used to track the
|
||||||
|
* service and characteristic being registered.
|
||||||
|
*/
|
||||||
|
void NimBLEServer::gattRegisterCallback(ble_gatt_register_ctxt* ctxt, void* arg) {
|
||||||
|
gattRegisterCallbackArgs* args = static_cast<gattRegisterCallbackArgs*>(arg);
|
||||||
|
|
||||||
|
if (ctxt->op == BLE_GATT_REGISTER_OP_SVC) {
|
||||||
|
NimBLEUUID uuid(ctxt->svc.svc_def->uuid);
|
||||||
|
args->pSvc = nullptr;
|
||||||
|
for (auto pSvc : NimBLEDevice::getServer()->m_svcVec) {
|
||||||
|
if (!pSvc->getRemoved() && pSvc->m_handle == 0 && pSvc->getUUID() == uuid) {
|
||||||
|
pSvc->m_handle = ctxt->svc.handle;
|
||||||
|
NIMBLE_LOGD(LOG_TAG, "Service registered: %s, handle=%d", uuid.toString().c_str(), ctxt->svc.handle);
|
||||||
|
// Set the arg to the service so we know that the following
|
||||||
|
// characteristics and descriptors belong to this service
|
||||||
|
args->pSvc = pSvc;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args->pSvc == nullptr) {
|
||||||
|
// If the service is not found then this is likely a characteristic or descriptor that was registered as
|
||||||
|
// part of the GATT server setup and not found in the service vector
|
||||||
|
NIMBLE_LOGD(LOG_TAG, "Skipping characteristic or descriptor registered with unknown service");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctxt->op == BLE_GATT_REGISTER_OP_CHR) {
|
||||||
|
NimBLEUUID uuid(ctxt->chr.chr_def->uuid);
|
||||||
|
args->pChar = nullptr;
|
||||||
|
for (auto pChr : args->pSvc->m_vChars) {
|
||||||
|
if (!pChr->getRemoved() && pChr->m_handle == 0 && pChr->getUUID() == uuid) {
|
||||||
|
pChr->m_handle = ctxt->chr.val_handle;
|
||||||
|
// Set the arg to the characteristic so we know that the following descriptors belong to this characteristic
|
||||||
|
args->pChar = pChr;
|
||||||
|
NIMBLE_LOGD(LOG_TAG,
|
||||||
|
"Characteristic registered: %s, def_handle=%d, val_handle=%d",
|
||||||
|
uuid.toString().c_str(),
|
||||||
|
ctxt->chr.def_handle,
|
||||||
|
ctxt->chr.val_handle);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctxt->op == BLE_GATT_REGISTER_OP_DSC) {
|
||||||
|
if (args->pChar == nullptr) {
|
||||||
|
NIMBLE_LOGE(LOG_TAG, "Descriptor registered with unknown characteristic, skipping");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
NimBLEUUID uuid(ctxt->dsc.dsc_def->uuid);
|
||||||
|
for (auto pDsc : args->pChar->m_vDescriptors) {
|
||||||
|
if (!pDsc->getRemoved() && pDsc->m_handle == 0 && pDsc->getUUID() == uuid) {
|
||||||
|
pDsc->m_handle = ctxt->dsc.handle;
|
||||||
|
NIMBLE_LOGD(LOG_TAG, "Descriptor registered: %s, handle=%d", uuid.toString().c_str(), ctxt->dsc.handle);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Start the GATT server.
|
* @brief Start the GATT server.
|
||||||
* @details Required to be called after setup of all services and characteristics / descriptors
|
* @details Required to be called after setup of all services and characteristics / descriptors
|
||||||
* for the NimBLE host to register them.
|
* for the NimBLE host to register them.
|
||||||
*/
|
*/
|
||||||
void NimBLEServer::start() {
|
bool NimBLEServer::start() {
|
||||||
if (m_gattsStarted) {
|
if (m_svcChanged && !getConnectedCount()) {
|
||||||
return; // already started
|
NIMBLE_LOGD(LOG_TAG, "Services have changed since last start, resetting GATT server");
|
||||||
|
m_gattsStarted = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m_gattsStarted) {
|
||||||
|
return true; // already started
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!resetGATT()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ble_hs_cfg.gatts_register_cb = NimBLEServer::gattRegisterCallback;
|
||||||
|
gattRegisterCallbackArgs args{};
|
||||||
|
ble_hs_cfg.gatts_register_arg = &args;
|
||||||
|
|
||||||
int rc = ble_gatts_start();
|
int rc = ble_gatts_start();
|
||||||
if (rc != 0) {
|
if (rc != 0) {
|
||||||
NIMBLE_LOGE(LOG_TAG, "ble_gatts_start; rc=%d, %s", rc, NimBLEUtils::returnCodeToString(rc));
|
NIMBLE_LOGE(LOG_TAG, "ble_gatts_start; rc=%d, %s", rc, NimBLEUtils::returnCodeToString(rc));
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
# if CONFIG_NIMBLE_CPP_LOG_LEVEL >= 4
|
# if MYNEWT_VAL(NIMBLE_CPP_LOG_LEVEL) >= 4
|
||||||
ble_gatts_show_local();
|
ble_gatts_show_local();
|
||||||
# endif
|
|
||||||
|
|
||||||
// Get the assigned service handles and build a vector of characteristics
|
// Check that all services were registered and log if any are missing.
|
||||||
// with Notify / Indicate capabilities for event handling
|
|
||||||
for (const auto& svc : m_svcVec) {
|
for (const auto& svc : m_svcVec) {
|
||||||
if (svc->getRemoved() == 0) {
|
if (svc->getRemoved() == 0) {
|
||||||
rc = ble_gatts_find_svc(svc->getUUID().getBase(), &svc->m_handle);
|
rc = ble_gatts_find_svc(svc->getUUID().getBase(), NULL);
|
||||||
if (rc != 0) {
|
if (rc != 0) {
|
||||||
NIMBLE_LOGW(LOG_TAG,
|
NIMBLE_LOGD(LOG_TAG,
|
||||||
"GATT Server started without service: %s, Service %s",
|
"GATT Server started without service: %s, Service %s",
|
||||||
svc->getUUID().toString().c_str(),
|
svc->getUUID().toString().c_str(),
|
||||||
svc->isStarted() ? "missing" : "not started");
|
svc->isStarted() ? "missing" : "not started");
|
||||||
continue; // Skip this service as it was not started
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
# endif
|
||||||
|
|
||||||
// If the services have changed indicate it now
|
// If the services have changed indicate it now
|
||||||
if (m_svcChanged) {
|
if (m_svcChanged) {
|
||||||
@@ -221,6 +317,7 @@ void NimBLEServer::start() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
m_gattsStarted = true;
|
m_gattsStarted = true;
|
||||||
|
return true;
|
||||||
} // start
|
} // start
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -243,13 +340,13 @@ bool NimBLEServer::disconnect(uint16_t connHandle, uint8_t reason) const {
|
|||||||
* @brief Disconnect the specified client with optional reason.
|
* @brief Disconnect the specified client with optional reason.
|
||||||
* @param [in] connInfo Connection of the client to disconnect.
|
* @param [in] connInfo Connection of the client to disconnect.
|
||||||
* @param [in] reason code for disconnecting.
|
* @param [in] reason code for disconnecting.
|
||||||
* @return NimBLE host return code.
|
* @return True if successful.
|
||||||
*/
|
*/
|
||||||
bool NimBLEServer::disconnect(const NimBLEConnInfo& connInfo, uint8_t reason) const {
|
bool NimBLEServer::disconnect(const NimBLEConnInfo& connInfo, uint8_t reason) const {
|
||||||
return disconnect(connInfo.getConnHandle(), reason);
|
return disconnect(connInfo.getConnHandle(), reason);
|
||||||
} // disconnect
|
} // disconnect
|
||||||
|
|
||||||
# if !CONFIG_BT_NIMBLE_EXT_ADV || defined(_DOXYGEN_)
|
# if !MYNEWT_VAL(BLE_EXT_ADV) || defined(_DOXYGEN_)
|
||||||
/**
|
/**
|
||||||
* @brief Set the server to automatically start advertising when a client disconnects.
|
* @brief Set the server to automatically start advertising when a client disconnects.
|
||||||
* @param [in] enable true == advertise, false == don't advertise.
|
* @param [in] enable true == advertise, false == don't advertise.
|
||||||
@@ -304,7 +401,7 @@ NimBLEConnInfo NimBLEServer::getPeerInfo(uint8_t index) const {
|
|||||||
for (const auto& peer : m_connectedPeers) {
|
for (const auto& peer : m_connectedPeers) {
|
||||||
if (peer != BLE_HS_CONN_HANDLE_NONE) {
|
if (peer != BLE_HS_CONN_HANDLE_NONE) {
|
||||||
if (count == index) {
|
if (count == index) {
|
||||||
return getPeerInfoByHandle(m_connectedPeers[count]);
|
return getPeerInfoByHandle(peer);
|
||||||
}
|
}
|
||||||
count++;
|
count++;
|
||||||
}
|
}
|
||||||
@@ -353,9 +450,14 @@ int NimBLEServer::handleGapEvent(ble_gap_event* event, void* arg) {
|
|||||||
|
|
||||||
switch (event->type) {
|
switch (event->type) {
|
||||||
case BLE_GAP_EVENT_CONNECT: {
|
case BLE_GAP_EVENT_CONNECT: {
|
||||||
if (event->connect.status != 0) {
|
rc = event->connect.status;
|
||||||
NIMBLE_LOGE(LOG_TAG, "Connection failed");
|
if (rc == BLE_ERR_UNSUPP_REM_FEATURE) {
|
||||||
# if !CONFIG_BT_NIMBLE_EXT_ADV
|
rc = 0; // Workaround: Ignore unsupported remote feature error as it is not a real error.
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rc != 0) {
|
||||||
|
NIMBLE_LOGE(LOG_TAG, "Connection failed rc = %d %s", rc, NimBLEUtils::returnCodeToString(rc));
|
||||||
|
# if !MYNEWT_VAL(BLE_EXT_ADV) && MYNEWT_VAL(BLE_ROLE_BROADCASTER)
|
||||||
NimBLEDevice::startAdvertising();
|
NimBLEDevice::startAdvertising();
|
||||||
# endif
|
# endif
|
||||||
} else {
|
} else {
|
||||||
@@ -399,18 +501,16 @@ int NimBLEServer::handleGapEvent(ble_gap_event* event, void* arg) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# if MYNEWT_VAL(BLE_ROLE_CENTRAL)
|
||||||
if (pServer->m_pClient && pServer->m_pClient->m_connHandle == event->disconnect.conn.conn_handle) {
|
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.
|
// If this was also the client make sure it's flagged as disconnected.
|
||||||
pServer->m_pClient->m_connHandle = BLE_HS_CONN_HANDLE_NONE;
|
pServer->m_pClient->m_connHandle = BLE_HS_CONN_HANDLE_NONE;
|
||||||
}
|
}
|
||||||
|
# endif
|
||||||
if (pServer->m_svcChanged) {
|
|
||||||
pServer->resetGATT();
|
|
||||||
}
|
|
||||||
|
|
||||||
peerInfo.m_desc = event->disconnect.conn;
|
peerInfo.m_desc = event->disconnect.conn;
|
||||||
pServer->m_pServerCallbacks->onDisconnect(pServer, peerInfo, event->disconnect.reason);
|
pServer->m_pServerCallbacks->onDisconnect(pServer, peerInfo, event->disconnect.reason);
|
||||||
# if !CONFIG_BT_NIMBLE_EXT_ADV
|
# if !MYNEWT_VAL(BLE_EXT_ADV) && MYNEWT_VAL(BLE_ROLE_BROADCASTER)
|
||||||
if (pServer->m_advertiseOnDisconnect) {
|
if (pServer->m_advertiseOnDisconnect) {
|
||||||
pServer->startAdvertising();
|
pServer->startAdvertising();
|
||||||
}
|
}
|
||||||
@@ -419,33 +519,21 @@ int NimBLEServer::handleGapEvent(ble_gap_event* event, void* arg) {
|
|||||||
} // BLE_GAP_EVENT_DISCONNECT
|
} // BLE_GAP_EVENT_DISCONNECT
|
||||||
|
|
||||||
case BLE_GAP_EVENT_SUBSCRIBE: {
|
case BLE_GAP_EVENT_SUBSCRIBE: {
|
||||||
NIMBLE_LOGI(LOG_TAG,
|
rc = ble_gap_conn_find(event->subscribe.conn_handle, &peerInfo.m_desc);
|
||||||
"subscribe event; attr_handle=%d, subscribed: %s",
|
if (rc != 0) {
|
||||||
event->subscribe.attr_handle,
|
break;
|
||||||
((event->subscribe.cur_notify || event->subscribe.cur_indicate) ? "true" : "false"));
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
chr->m_pCallbacks->onSubscribe(chr,
|
|
||||||
peerInfo,
|
|
||||||
event->subscribe.cur_notify + event->subscribe.cur_indicate);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint8_t subVal = event->subscribe.cur_notify + (event->subscribe.cur_indicate << 1);
|
||||||
|
NIMBLE_LOGI(LOG_TAG, "subscribe event; attr_handle=%d, subscribed: %d", event->subscribe.attr_handle, subVal);
|
||||||
|
|
||||||
|
auto pChar = pServer->getCharacteristicByHandle(event->subscribe.attr_handle);
|
||||||
|
if (!pChar) {
|
||||||
|
NIMBLE_LOGE(LOG_TAG, "subscribe event; attr_handle=%d, not found", event->subscribe.attr_handle);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
pChar->processSubRequest(peerInfo, subVal);
|
||||||
break;
|
break;
|
||||||
} // BLE_GAP_EVENT_SUBSCRIBE
|
} // BLE_GAP_EVENT_SUBSCRIBE
|
||||||
|
|
||||||
@@ -459,18 +547,14 @@ int NimBLEServer::handleGapEvent(ble_gap_event* event, void* arg) {
|
|||||||
} // BLE_GAP_EVENT_MTU
|
} // BLE_GAP_EVENT_MTU
|
||||||
|
|
||||||
case BLE_GAP_EVENT_NOTIFY_TX: {
|
case BLE_GAP_EVENT_NOTIFY_TX: {
|
||||||
NimBLECharacteristic* pChar = nullptr;
|
rc = ble_gap_conn_find(event->notify_tx.conn_handle, &peerInfo.m_desc);
|
||||||
|
if (rc != 0) {
|
||||||
for (const auto& svc : pServer->m_svcVec) {
|
break;
|
||||||
for (auto& chr : svc->m_vChars) {
|
|
||||||
if (chr->getHandle() == event->notify_tx.attr_handle) {
|
|
||||||
pChar = chr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto pChar = pServer->getCharacteristicByHandle(event->notify_tx.attr_handle);
|
||||||
if (pChar == nullptr) {
|
if (pChar == nullptr) {
|
||||||
return 0;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event->notify_tx.indication) {
|
if (event->notify_tx.indication) {
|
||||||
@@ -480,14 +564,24 @@ int NimBLEServer::handleGapEvent(ble_gap_event* event, void* arg) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pChar->m_pCallbacks->onStatus(pChar, event->notify_tx.status);
|
pChar->m_pCallbacks->onStatus(pChar, event->notify_tx.status);
|
||||||
|
pChar->m_pCallbacks->onStatus(pChar, peerInfo, event->notify_tx.status);
|
||||||
break;
|
break;
|
||||||
} // BLE_GAP_EVENT_NOTIFY_TX
|
} // BLE_GAP_EVENT_NOTIFY_TX
|
||||||
|
|
||||||
|
# if MYNEWT_VAL(BLE_ROLE_CENTRAL)
|
||||||
|
case BLE_GAP_EVENT_NOTIFY_RX: {
|
||||||
|
if (pServer->m_pClient && pServer->m_pClient->m_connHandle == event->notify_rx.conn_handle) {
|
||||||
|
NimBLEClient::handleGapEvent(event, pServer->m_pClient);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
} // BLE_GAP_EVENT_NOTIFY_RX
|
||||||
|
# endif
|
||||||
|
|
||||||
case BLE_GAP_EVENT_ADV_COMPLETE: {
|
case BLE_GAP_EVENT_ADV_COMPLETE: {
|
||||||
# if CONFIG_BT_NIMBLE_EXT_ADV
|
# if MYNEWT_VAL(BLE_EXT_ADV) && MYNEWT_VAL(BLE_ROLE_BROADCASTER)
|
||||||
case BLE_GAP_EVENT_SCAN_REQ_RCVD:
|
case BLE_GAP_EVENT_SCAN_REQ_RCVD:
|
||||||
return NimBLEExtAdvertising::handleGapEvent(event, arg);
|
return NimBLEExtAdvertising::handleGapEvent(event, arg);
|
||||||
# else
|
# elif MYNEWT_VAL(BLE_ROLE_BROADCASTER)
|
||||||
return NimBLEAdvertising::handleGapEvent(event, arg);
|
return NimBLEAdvertising::handleGapEvent(event, arg);
|
||||||
# endif
|
# endif
|
||||||
} // BLE_GAP_EVENT_ADV_COMPLETE | BLE_GAP_EVENT_SCAN_REQ_RCVD
|
} // BLE_GAP_EVENT_ADV_COMPLETE | BLE_GAP_EVENT_SCAN_REQ_RCVD
|
||||||
@@ -527,6 +621,18 @@ int NimBLEServer::handleGapEvent(ble_gap_event* event, void* arg) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pServer->m_pServerCallbacks->onAuthenticationComplete(peerInfo);
|
pServer->m_pServerCallbacks->onAuthenticationComplete(peerInfo);
|
||||||
|
# if MYNEWT_VAL(BLE_ROLE_CENTRAL)
|
||||||
|
if (pServer->m_pClient && pServer->m_pClient->m_connHandle == event->enc_change.conn_handle) {
|
||||||
|
NimBLEClient::handleGapEvent(event, pServer->m_pClient);
|
||||||
|
}
|
||||||
|
# endif
|
||||||
|
// update the secured status of the peer in each characteristic's subscribed peers list
|
||||||
|
for (const auto& svc : pServer->m_svcVec) {
|
||||||
|
for (const auto& chr : svc->m_vChars) {
|
||||||
|
chr->updatePeerStatus(peerInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
} // BLE_GAP_EVENT_ENC_CHANGE
|
} // BLE_GAP_EVENT_ENC_CHANGE
|
||||||
|
|
||||||
@@ -540,7 +646,6 @@ int NimBLEServer::handleGapEvent(ble_gap_event* event, void* arg) {
|
|||||||
break;
|
break;
|
||||||
} // BLE_GAP_EVENT_IDENTITY_RESOLVED
|
} // BLE_GAP_EVENT_IDENTITY_RESOLVED
|
||||||
|
|
||||||
# if CONFIG_BT_NIMBLE_EXT_ADV
|
|
||||||
case BLE_GAP_EVENT_PHY_UPDATE_COMPLETE: {
|
case BLE_GAP_EVENT_PHY_UPDATE_COMPLETE: {
|
||||||
rc = ble_gap_conn_find(event->phy_updated.conn_handle, &peerInfo.m_desc);
|
rc = ble_gap_conn_find(event->phy_updated.conn_handle, &peerInfo.m_desc);
|
||||||
if (rc != 0) {
|
if (rc != 0) {
|
||||||
@@ -550,7 +655,6 @@ int NimBLEServer::handleGapEvent(ble_gap_event* event, void* arg) {
|
|||||||
pServer->m_pServerCallbacks->onPhyUpdate(peerInfo, event->phy_updated.tx_phy, event->phy_updated.rx_phy);
|
pServer->m_pServerCallbacks->onPhyUpdate(peerInfo, event->phy_updated.tx_phy, event->phy_updated.rx_phy);
|
||||||
return 0;
|
return 0;
|
||||||
} // BLE_GAP_EVENT_PHY_UPDATE_COMPLETE
|
} // BLE_GAP_EVENT_PHY_UPDATE_COMPLETE
|
||||||
# endif
|
|
||||||
|
|
||||||
case BLE_GAP_EVENT_PASSKEY_ACTION: {
|
case BLE_GAP_EVENT_PASSKEY_ACTION: {
|
||||||
struct ble_sm_io pkey = {0, 0};
|
struct ble_sm_io pkey = {0, 0};
|
||||||
@@ -585,6 +689,15 @@ int NimBLEServer::handleGapEvent(ble_gap_event* event, void* arg) {
|
|||||||
// }
|
// }
|
||||||
// rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey);
|
// 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);
|
// NIMBLE_LOGD(LOG_TAG, "BLE_SM_IOACT_OOB; ble_sm_inject_io result: %d", rc);
|
||||||
|
} else if (event->passkey.params.action == BLE_SM_IOACT_INPUT) {
|
||||||
|
NIMBLE_LOGD(LOG_TAG, "Enter the passkey");
|
||||||
|
|
||||||
|
rc = ble_gap_conn_find(event->passkey.conn_handle, &peerInfo.m_desc);
|
||||||
|
if (rc != 0) {
|
||||||
|
return BLE_ATT_ERR_INVALID_HANDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
pServer->m_pServerCallbacks->onPassKeyEntry(peerInfo);
|
||||||
} else if (event->passkey.params.action == BLE_SM_IOACT_NONE) {
|
} else if (event->passkey.params.action == BLE_SM_IOACT_NONE) {
|
||||||
NIMBLE_LOGD(LOG_TAG, "No passkey action required");
|
NIMBLE_LOGD(LOG_TAG, "No passkey action required");
|
||||||
}
|
}
|
||||||
@@ -607,21 +720,19 @@ int NimBLEServer::handleGattEvent(uint16_t connHandle, uint16_t attrHandle, ble_
|
|||||||
NIMBLE_LOGD(LOG_TAG,
|
NIMBLE_LOGD(LOG_TAG,
|
||||||
"Gatt %s event",
|
"Gatt %s event",
|
||||||
(ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR || ctxt->op == BLE_GATT_ACCESS_OP_READ_DSC) ? "Read" : "Write");
|
(ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR || ctxt->op == BLE_GATT_ACCESS_OP_READ_DSC) ? "Read" : "Write");
|
||||||
auto pAtt = static_cast<NimBLELocalValueAttribute*>(arg);
|
auto pAtt = static_cast<NimBLELocalValueAttribute*>(arg);
|
||||||
auto val = pAtt->getAttVal();
|
const NimBLEAttValue& val = pAtt->getAttVal();
|
||||||
|
|
||||||
NimBLEConnInfo peerInfo{};
|
NimBLEConnInfo peerInfo{};
|
||||||
ble_gap_conn_find(connHandle, &peerInfo.m_desc);
|
ble_gap_conn_find(connHandle, &peerInfo.m_desc);
|
||||||
|
|
||||||
switch (ctxt->op) {
|
switch (ctxt->op) {
|
||||||
case BLE_GATT_ACCESS_OP_READ_DSC:
|
case BLE_GATT_ACCESS_OP_READ_DSC:
|
||||||
case BLE_GATT_ACCESS_OP_READ_CHR: {
|
case BLE_GATT_ACCESS_OP_READ_CHR: {
|
||||||
// Don't call readEvent if this is an internal read (handle is NONE)
|
// Don't call readEvent if the buffer len is 0 (this is a follow up to a previous read),
|
||||||
if (connHandle != BLE_HS_CONN_HANDLE_NONE) {
|
// or if this is an internal read (handle is NONE)
|
||||||
// If the packet header is only 8 bytes then this is a follow up of a long read
|
if (ctxt->om->om_len > 0 && connHandle != BLE_HS_CONN_HANDLE_NONE) {
|
||||||
// so we don't want to call the onRead() callback again.
|
pAtt->readEvent(peerInfo);
|
||||||
if (ctxt->om->om_pkthdr_len > 8 || pAtt->getAttVal().size() <= (ble_att_mtu(connHandle) - 3)) {
|
|
||||||
pAtt->readEvent(peerInfo);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ble_npl_hw_enter_critical();
|
ble_npl_hw_enter_critical();
|
||||||
@@ -633,12 +744,12 @@ int NimBLEServer::handleGattEvent(uint16_t connHandle, uint16_t attrHandle, ble_
|
|||||||
case BLE_GATT_ACCESS_OP_WRITE_DSC:
|
case BLE_GATT_ACCESS_OP_WRITE_DSC:
|
||||||
case BLE_GATT_ACCESS_OP_WRITE_CHR: {
|
case BLE_GATT_ACCESS_OP_WRITE_CHR: {
|
||||||
uint16_t maxLen = val.max_size();
|
uint16_t maxLen = val.max_size();
|
||||||
if (ctxt->om->om_len > maxLen) {
|
uint16_t len = ctxt->om->om_len;
|
||||||
|
if (len > maxLen) {
|
||||||
return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
|
return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t buf[maxLen];
|
uint8_t buf[maxLen];
|
||||||
uint16_t len = ctxt->om->om_len;
|
|
||||||
memcpy(buf, ctxt->om->om_data, len);
|
memcpy(buf, ctxt->om->om_data, len);
|
||||||
|
|
||||||
os_mbuf* next;
|
os_mbuf* next;
|
||||||
@@ -726,7 +837,7 @@ void NimBLEServer::removeService(NimBLEService* service, bool deleteSvc) {
|
|||||||
|
|
||||||
service->setRemoved(deleteSvc ? NIMBLE_ATT_REMOVE_DELETE : NIMBLE_ATT_REMOVE_HIDE);
|
service->setRemoved(deleteSvc ? NIMBLE_ATT_REMOVE_DELETE : NIMBLE_ATT_REMOVE_HIDE);
|
||||||
serviceChanged();
|
serviceChanged();
|
||||||
# if !CONFIG_BT_NIMBLE_EXT_ADV
|
# if !MYNEWT_VAL(BLE_EXT_ADV) && MYNEWT_VAL(BLE_ROLE_BROADCASTER)
|
||||||
NimBLEDevice::getAdvertising()->removeServiceUUID(service->getUUID());
|
NimBLEDevice::getAdvertising()->removeServiceUUID(service->getUUID());
|
||||||
# endif
|
# endif
|
||||||
} // removeService
|
} // removeService
|
||||||
@@ -757,58 +868,67 @@ void NimBLEServer::addService(NimBLEService* service) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Resets the GATT server, used when services are added/removed after initialization.
|
* @brief Resets the GATT server, used when services are added/removed after initialization.
|
||||||
|
* @return True if successful.
|
||||||
|
* @details This will reset the GATT server and re-register all services, characteristics, and
|
||||||
|
* descriptors that have not been removed. Services, characteristics, and descriptors that have been
|
||||||
|
* removed but not deleted will be skipped and have their handles cleared, and those that have been
|
||||||
|
* deleted will be removed from the server's service vector.
|
||||||
*/
|
*/
|
||||||
void NimBLEServer::resetGATT() {
|
bool NimBLEServer::resetGATT() {
|
||||||
if (getConnectedCount() > 0) {
|
# if MYNEWT_VAL(BLE_ROLE_BROADCASTER)
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
NimBLEDevice::stopAdvertising();
|
NimBLEDevice::stopAdvertising();
|
||||||
|
# endif
|
||||||
|
|
||||||
ble_gatts_reset();
|
ble_gatts_reset();
|
||||||
ble_svc_gap_init();
|
ble_svc_gap_init();
|
||||||
ble_svc_gatt_init();
|
ble_svc_gatt_init();
|
||||||
|
|
||||||
for (auto it = m_svcVec.begin(); it != m_svcVec.end();) {
|
for (auto svcIt = m_svcVec.begin(); svcIt != m_svcVec.end();) {
|
||||||
if ((*it)->getRemoved() > 0) {
|
auto* pSvc = *svcIt;
|
||||||
if ((*it)->getRemoved() == NIMBLE_ATT_REMOVE_DELETE) {
|
if (pSvc->getRemoved() == NIMBLE_ATT_REMOVE_DELETE) {
|
||||||
delete *it;
|
delete pSvc;
|
||||||
it = m_svcVec.erase(it);
|
svcIt = m_svcVec.erase(svcIt);
|
||||||
} else {
|
|
||||||
++it;
|
|
||||||
}
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
(*it)->start();
|
for (auto chrIt = pSvc->m_vChars.begin(); chrIt != pSvc->m_vChars.end();) {
|
||||||
++it;
|
auto* pChr = *chrIt;
|
||||||
|
if (pChr->getRemoved() == NIMBLE_ATT_REMOVE_DELETE) {
|
||||||
|
delete pChr;
|
||||||
|
chrIt = pSvc->m_vChars.erase(chrIt);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto dscIt = pChr->m_vDescriptors.begin(); dscIt != pChr->m_vDescriptors.end();) {
|
||||||
|
auto* pDsc = *dscIt;
|
||||||
|
if (pDsc->getRemoved() == NIMBLE_ATT_REMOVE_DELETE) {
|
||||||
|
delete pDsc;
|
||||||
|
dscIt = pChr->m_vDescriptors.erase(dscIt);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
pDsc->m_handle = 0;
|
||||||
|
++dscIt;
|
||||||
|
}
|
||||||
|
|
||||||
|
pChr->m_handle = 0;
|
||||||
|
++chrIt;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pSvc->getRemoved() == 0) {
|
||||||
|
if (!pSvc->start_internal()) {
|
||||||
|
NIMBLE_LOGE(LOG_TAG, "Failed to start service: %s", pSvc->getUUID().toString().c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pSvc->m_handle = 0;
|
||||||
|
++svcIt;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_gattsStarted = false;
|
return true;
|
||||||
} // resetGATT
|
} // resetGATT
|
||||||
|
|
||||||
# if CONFIG_BT_NIMBLE_EXT_ADV
|
|
||||||
/**
|
|
||||||
* @brief Start advertising.
|
|
||||||
* @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] 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 instId, int duration, int maxEvents) const {
|
|
||||||
return getAdvertising()->start(instId, duration, maxEvents);
|
|
||||||
} // startAdvertising
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Convenience function to stop advertising a data set.
|
|
||||||
* @param [in] instId The extended advertisement instance ID to stop advertising.
|
|
||||||
* @return True if advertising stopped successfully.
|
|
||||||
*/
|
|
||||||
bool NimBLEServer::stopAdvertising(uint8_t instId) const {
|
|
||||||
return getAdvertising()->stop(instId);
|
|
||||||
} // stopAdvertising
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Request an update to the PHY used for a peer connection.
|
* @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] connHandle the connection handle to the update the PHY for.
|
||||||
@@ -852,9 +972,33 @@ bool NimBLEServer::getPhy(uint16_t connHandle, uint8_t* txPhy, uint8_t* rxPhy) {
|
|||||||
|
|
||||||
return rc == 0;
|
return rc == 0;
|
||||||
} // getPhy
|
} // getPhy
|
||||||
|
|
||||||
|
# if MYNEWT_VAL(BLE_EXT_ADV)
|
||||||
|
/**
|
||||||
|
* @brief Start advertising.
|
||||||
|
* @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] 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 instId, int duration, int maxEvents) const {
|
||||||
|
return getAdvertising()->start(instId, duration, maxEvents);
|
||||||
|
} // startAdvertising
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Convenience function to stop advertising a data set.
|
||||||
|
* @param [in] instId The extended advertisement instance ID to stop advertising.
|
||||||
|
* @return True if advertising stopped successfully.
|
||||||
|
*/
|
||||||
|
bool NimBLEServer::stopAdvertising(uint8_t instId) const {
|
||||||
|
return getAdvertising()->stop(instId);
|
||||||
|
} // stopAdvertising
|
||||||
|
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
# if !CONFIG_BT_NIMBLE_EXT_ADV || defined(_DOXYGEN_)
|
# if (!MYNEWT_VAL(BLE_EXT_ADV) && MYNEWT_VAL(BLE_ROLE_BROADCASTER)) || defined(_DOXYGEN_)
|
||||||
/**
|
/**
|
||||||
* @brief Start advertising.
|
* @brief Start advertising.
|
||||||
* @param [in] duration The duration in milliseconds to advertise for, default = forever.
|
* @param [in] duration The duration in milliseconds to advertise for, default = forever.
|
||||||
@@ -931,7 +1075,7 @@ void NimBLEServer::setDataLen(uint16_t connHandle, uint16_t octets) const {
|
|||||||
# endif
|
# endif
|
||||||
} // setDataLen
|
} // setDataLen
|
||||||
|
|
||||||
# if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
|
# if MYNEWT_VAL(BLE_ROLE_CENTRAL)
|
||||||
/**
|
/**
|
||||||
* @brief Create a client instance from the connection handle.
|
* @brief Create a client instance from the connection handle.
|
||||||
* @param [in] connHandle The connection handle to create a client instance from.
|
* @param [in] connHandle The connection handle to create a client instance from.
|
||||||
@@ -997,6 +1141,11 @@ uint32_t NimBLEServerCallbacks::onPassKeyDisplay() {
|
|||||||
return 123456;
|
return 123456;
|
||||||
} // onPassKeyDisplay
|
} // onPassKeyDisplay
|
||||||
|
|
||||||
|
void NimBLEServerCallbacks::onPassKeyEntry(NimBLEConnInfo& connInfo) {
|
||||||
|
NIMBLE_LOGD("NimBLEServerCallbacks", "onPassKeyEntry: default: 123456");
|
||||||
|
NimBLEDevice::injectPassKey(connInfo, 123456);
|
||||||
|
} // onPassKeyEntry
|
||||||
|
|
||||||
void NimBLEServerCallbacks::onConfirmPassKey(NimBLEConnInfo& connInfo, uint32_t pin) {
|
void NimBLEServerCallbacks::onConfirmPassKey(NimBLEConnInfo& connInfo, uint32_t pin) {
|
||||||
NIMBLE_LOGD("NimBLEServerCallbacks", "onConfirmPasskey: default: true");
|
NIMBLE_LOGD("NimBLEServerCallbacks", "onConfirmPasskey: default: true");
|
||||||
NimBLEDevice::injectConfirmPasskey(connInfo, true);
|
NimBLEDevice::injectConfirmPasskey(connInfo, true);
|
||||||
@@ -1014,10 +1163,8 @@ void NimBLEServerCallbacks::onConnParamsUpdate(NimBLEConnInfo& connInfo) {
|
|||||||
NIMBLE_LOGD("NimBLEServerCallbacks", "onConnParamsUpdate: default");
|
NIMBLE_LOGD("NimBLEServerCallbacks", "onConnParamsUpdate: default");
|
||||||
} // onConnParamsUpdate
|
} // onConnParamsUpdate
|
||||||
|
|
||||||
# if CONFIG_BT_NIMBLE_EXT_ADV
|
|
||||||
void NimBLEServerCallbacks::onPhyUpdate(NimBLEConnInfo& connInfo, uint8_t txPhy, uint8_t rxPhy) {
|
void NimBLEServerCallbacks::onPhyUpdate(NimBLEConnInfo& connInfo, uint8_t txPhy, uint8_t rxPhy) {
|
||||||
NIMBLE_LOGD("NimBLEServerCallbacks", "onPhyUpdate: default, txPhy: %d, rxPhy: %d", txPhy, rxPhy);
|
NIMBLE_LOGD("NimBLEServerCallbacks", "onPhyUpdate: default, txPhy: %d, rxPhy: %d", txPhy, rxPhy);
|
||||||
} // onPhyUpdate
|
} // onPhyUpdate
|
||||||
# endif
|
|
||||||
|
|
||||||
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL */
|
#endif // CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_PERIPHERAL)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
|
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
|
||||||
* esp-nimble-cpp, NimBLE-Arduino contributors.
|
* esp-nimble-cpp, NimBLE-Arduino contributors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@@ -18,8 +18,8 @@
|
|||||||
#ifndef NIMBLE_CPP_SERVER_H_
|
#ifndef NIMBLE_CPP_SERVER_H_
|
||||||
#define NIMBLE_CPP_SERVER_H_
|
#define NIMBLE_CPP_SERVER_H_
|
||||||
|
|
||||||
#include "nimconfig.h"
|
#include "syscfg/syscfg.h"
|
||||||
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
|
#if CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_PERIPHERAL)
|
||||||
|
|
||||||
# if defined(CONFIG_NIMBLE_CPP_IDF)
|
# if defined(CONFIG_NIMBLE_CPP_IDF)
|
||||||
# include "host/ble_gap.h"
|
# include "host/ble_gap.h"
|
||||||
@@ -45,12 +45,14 @@ class NimBLEConnInfo;
|
|||||||
class NimBLEAddress;
|
class NimBLEAddress;
|
||||||
class NimBLEService;
|
class NimBLEService;
|
||||||
class NimBLECharacteristic;
|
class NimBLECharacteristic;
|
||||||
# if CONFIG_BT_NIMBLE_EXT_ADV
|
# if MYNEWT_VAL(BLE_ROLE_BROADCASTER)
|
||||||
|
# if MYNEWT_VAL(BLE_EXT_ADV)
|
||||||
class NimBLEExtAdvertising;
|
class NimBLEExtAdvertising;
|
||||||
# else
|
# else
|
||||||
class NimBLEAdvertising;
|
class NimBLEAdvertising;
|
||||||
|
# endif
|
||||||
# endif
|
# endif
|
||||||
# if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
|
# if MYNEWT_VAL(BLE_ROLE_CENTRAL)
|
||||||
class NimBLEClient;
|
class NimBLEClient;
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
@@ -59,7 +61,7 @@ class NimBLEClient;
|
|||||||
*/
|
*/
|
||||||
class NimBLEServer {
|
class NimBLEServer {
|
||||||
public:
|
public:
|
||||||
void start();
|
bool start();
|
||||||
uint8_t getConnectedCount() const;
|
uint8_t getConnectedCount() const;
|
||||||
bool disconnect(uint16_t connHandle, uint8_t reason = BLE_ERR_REM_USER_CONN_TERM) 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;
|
bool disconnect(const NimBLEConnInfo& connInfo, uint8_t reason = BLE_ERR_REM_USER_CONN_TERM) const;
|
||||||
@@ -70,6 +72,7 @@ class NimBLEServer {
|
|||||||
NimBLEService* getServiceByUUID(const char* uuid, uint16_t instanceId = 0) const;
|
NimBLEService* getServiceByUUID(const char* uuid, uint16_t instanceId = 0) const;
|
||||||
NimBLEService* getServiceByUUID(const NimBLEUUID& uuid, uint16_t instanceId = 0) const;
|
NimBLEService* getServiceByUUID(const NimBLEUUID& uuid, uint16_t instanceId = 0) const;
|
||||||
NimBLEService* getServiceByHandle(uint16_t handle) const;
|
NimBLEService* getServiceByHandle(uint16_t handle) const;
|
||||||
|
NimBLECharacteristic* getCharacteristicByHandle(uint16_t handle) const;
|
||||||
void removeService(NimBLEService* service, bool deleteSvc = false);
|
void removeService(NimBLEService* service, bool deleteSvc = false);
|
||||||
void addService(NimBLEService* service);
|
void addService(NimBLEService* service);
|
||||||
uint16_t getPeerMTU(uint16_t connHandle) const;
|
uint16_t getPeerMTU(uint16_t connHandle) const;
|
||||||
@@ -79,59 +82,62 @@ class NimBLEServer {
|
|||||||
NimBLEConnInfo getPeerInfoByHandle(uint16_t connHandle) const;
|
NimBLEConnInfo getPeerInfoByHandle(uint16_t connHandle) const;
|
||||||
void advertiseOnDisconnect(bool enable);
|
void advertiseOnDisconnect(bool enable);
|
||||||
void setDataLen(uint16_t connHandle, uint16_t tx_octets) const;
|
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 defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
|
# if MYNEWT_VAL(BLE_ROLE_CENTRAL)
|
||||||
NimBLEClient* getClient(uint16_t connHandle);
|
NimBLEClient* getClient(uint16_t connHandle);
|
||||||
NimBLEClient* getClient(const NimBLEConnInfo& connInfo);
|
NimBLEClient* getClient(const NimBLEConnInfo& connInfo);
|
||||||
void deleteClient();
|
void deleteClient();
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
# if CONFIG_BT_NIMBLE_EXT_ADV
|
# if MYNEWT_VAL(BLE_ROLE_BROADCASTER)
|
||||||
|
# if MYNEWT_VAL(BLE_EXT_ADV)
|
||||||
NimBLEExtAdvertising* getAdvertising() const;
|
NimBLEExtAdvertising* getAdvertising() const;
|
||||||
bool startAdvertising(uint8_t instanceId, int duration = 0, int maxEvents = 0) const;
|
bool startAdvertising(uint8_t instanceId, int duration = 0, int maxEvents = 0) const;
|
||||||
bool stopAdvertising(uint8_t instanceId) const;
|
bool stopAdvertising(uint8_t instanceId) const;
|
||||||
bool updatePhy(uint16_t connHandle, uint8_t txPhysMask, uint8_t rxPhysMask, uint16_t phyOptions);
|
# endif
|
||||||
bool getPhy(uint16_t connHandle, uint8_t* txPhy, uint8_t* rxPhy);
|
|
||||||
# endif
|
|
||||||
|
|
||||||
# if !CONFIG_BT_NIMBLE_EXT_ADV || defined(_DOXYGEN_)
|
# if !MYNEWT_VAL(BLE_EXT_ADV) || defined(_DOXYGEN_)
|
||||||
NimBLEAdvertising* getAdvertising() const;
|
NimBLEAdvertising* getAdvertising() const;
|
||||||
bool startAdvertising(uint32_t duration = 0) const;
|
bool startAdvertising(uint32_t duration = 0) const;
|
||||||
bool stopAdvertising() const;
|
bool stopAdvertising() const;
|
||||||
|
# endif
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class NimBLEDevice;
|
friend class NimBLEDevice;
|
||||||
friend class NimBLEService;
|
friend class NimBLEService;
|
||||||
friend class NimBLECharacteristic;
|
friend class NimBLECharacteristic;
|
||||||
# if CONFIG_BT_NIMBLE_EXT_ADV
|
# if MYNEWT_VAL(BLE_ROLE_BROADCASTER)
|
||||||
|
# if MYNEWT_VAL(BLE_EXT_ADV)
|
||||||
friend class NimBLEExtAdvertising;
|
friend class NimBLEExtAdvertising;
|
||||||
# else
|
# else
|
||||||
friend class NimBLEAdvertising;
|
friend class NimBLEAdvertising;
|
||||||
|
# endif
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
NimBLEServer();
|
NimBLEServer();
|
||||||
~NimBLEServer();
|
~NimBLEServer();
|
||||||
|
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);
|
||||||
|
static void gattRegisterCallback(struct ble_gatt_register_ctxt* ctxt, void* arg);
|
||||||
|
void serviceChanged();
|
||||||
|
bool resetGATT();
|
||||||
|
|
||||||
bool m_gattsStarted : 1;
|
bool m_gattsStarted : 1;
|
||||||
bool m_svcChanged : 1;
|
bool m_svcChanged : 1;
|
||||||
bool m_deleteCallbacks : 1;
|
bool m_deleteCallbacks : 1;
|
||||||
# if !CONFIG_BT_NIMBLE_EXT_ADV
|
# if !MYNEWT_VAL(BLE_EXT_ADV) && MYNEWT_VAL(BLE_ROLE_BROADCASTER)
|
||||||
bool m_advertiseOnDisconnect : 1;
|
bool m_advertiseOnDisconnect : 1;
|
||||||
# endif
|
# endif
|
||||||
NimBLEServerCallbacks* m_pServerCallbacks;
|
NimBLEServerCallbacks* m_pServerCallbacks;
|
||||||
std::vector<NimBLEService*> m_svcVec;
|
std::vector<NimBLEService*> m_svcVec;
|
||||||
std::array<uint16_t, CONFIG_BT_NIMBLE_MAX_CONNECTIONS> m_connectedPeers;
|
std::array<uint16_t, MYNEWT_VAL(BLE_MAX_CONNECTIONS)> m_connectedPeers;
|
||||||
|
|
||||||
# if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
|
# if MYNEWT_VAL(BLE_ROLE_CENTRAL)
|
||||||
NimBLEClient* m_pClient{nullptr};
|
NimBLEClient* m_pClient{nullptr};
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
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
|
}; // NimBLEServer
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -174,6 +180,15 @@ class NimBLEServerCallbacks {
|
|||||||
*/
|
*/
|
||||||
virtual uint32_t onPassKeyDisplay();
|
virtual uint32_t onPassKeyDisplay();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Called when using passkey entry pairing and the peer requires the passkey to be entered.
|
||||||
|
* @param [in] connInfo A reference to a NimBLEConnInfo instance with information
|
||||||
|
* about the peer connection parameters.
|
||||||
|
* @details The application should call NimBLEDevice::injectPassKey with the passkey
|
||||||
|
* displayed on the peer device to complete the pairing process.
|
||||||
|
*/
|
||||||
|
virtual void onPassKeyEntry(NimBLEConnInfo& connInfo);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Called when using numeric comparision for pairing.
|
* @brief Called when using numeric comparision for pairing.
|
||||||
* @param [in] connInfo A reference to a NimBLEConnInfo instance with information
|
* @param [in] connInfo A reference to a NimBLEConnInfo instance with information
|
||||||
@@ -203,7 +218,6 @@ class NimBLEServerCallbacks {
|
|||||||
*/
|
*/
|
||||||
virtual void onConnParamsUpdate(NimBLEConnInfo& connInfo);
|
virtual void onConnParamsUpdate(NimBLEConnInfo& connInfo);
|
||||||
|
|
||||||
# if CONFIG_BT_NIMBLE_EXT_ADV
|
|
||||||
/**
|
/**
|
||||||
* @brief Called when the PHY update procedure is complete.
|
* @brief Called when the PHY update procedure is complete.
|
||||||
* @param [in] connInfo A reference to a NimBLEConnInfo instance with information
|
* @param [in] connInfo A reference to a NimBLEConnInfo instance with information
|
||||||
@@ -216,8 +230,7 @@ class NimBLEServerCallbacks {
|
|||||||
* * BLE_GAP_LE_PHY_CODED
|
* * BLE_GAP_LE_PHY_CODED
|
||||||
*/
|
*/
|
||||||
virtual void onPhyUpdate(NimBLEConnInfo& connInfo, uint8_t txPhy, uint8_t rxPhy);
|
virtual void onPhyUpdate(NimBLEConnInfo& connInfo, uint8_t txPhy, uint8_t rxPhy);
|
||||||
# endif
|
|
||||||
}; // NimBLEServerCallbacks
|
}; // NimBLEServerCallbacks
|
||||||
|
|
||||||
#endif // CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL
|
#endif // CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_PERIPHERAL)
|
||||||
#endif // NIMBLE_CPP_SERVER_H_
|
#endif // NIMBLE_CPP_SERVER_H_
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
|
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
|
||||||
* esp-nimble-cpp, NimBLE-Arduino contributors.
|
* esp-nimble-cpp, NimBLE-Arduino contributors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@@ -15,11 +15,10 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "nimconfig.h"
|
#include "NimBLEService.h"
|
||||||
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
|
#if CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_PERIPHERAL)
|
||||||
|
|
||||||
# include "NimBLEService.h"
|
# if MYNEWT_VAL(BLE_EXT_ADV)
|
||||||
# if CONFIG_BT_NIMBLE_EXT_ADV
|
|
||||||
# include "NimBLEExtAdvertising.h"
|
# include "NimBLEExtAdvertising.h"
|
||||||
# else
|
# else
|
||||||
# include "NimBLEAdvertising.h"
|
# include "NimBLEAdvertising.h"
|
||||||
@@ -49,12 +48,7 @@ NimBLEService::NimBLEService(const NimBLEUUID& uuid)
|
|||||||
* @brief Destructor, make sure we release the resources allocated for the service.
|
* @brief Destructor, make sure we release the resources allocated for the service.
|
||||||
*/
|
*/
|
||||||
NimBLEService::~NimBLEService() {
|
NimBLEService::~NimBLEService() {
|
||||||
if (m_pSvcDef->characteristics) {
|
clearServiceDefinitions();
|
||||||
if (m_pSvcDef->characteristics->descriptors) {
|
|
||||||
delete[] m_pSvcDef->characteristics->descriptors;
|
|
||||||
}
|
|
||||||
delete[] m_pSvcDef->characteristics;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const auto& it : m_vChars) {
|
for (const auto& it : m_vChars) {
|
||||||
delete it;
|
delete it;
|
||||||
@@ -89,15 +83,10 @@ void NimBLEService::dump() const {
|
|||||||
* and registers it with the NimBLE stack.
|
* and registers it with the NimBLE stack.
|
||||||
* @return bool success/failure .
|
* @return bool success/failure .
|
||||||
*/
|
*/
|
||||||
bool NimBLEService::start() {
|
bool NimBLEService::start_internal() {
|
||||||
NIMBLE_LOGD(LOG_TAG, ">> start(): Starting service: %s", toString().c_str());
|
NIMBLE_LOGD(LOG_TAG, ">> start(): Starting service: UUID: %s", getUUID().toString().c_str());
|
||||||
// If started previously, clear everything and start over
|
// Make sure the definitions are cleared first
|
||||||
if (m_pSvcDef->characteristics) {
|
clearServiceDefinitions();
|
||||||
if (m_pSvcDef->characteristics->descriptors) {
|
|
||||||
delete[] m_pSvcDef->characteristics->descriptors;
|
|
||||||
}
|
|
||||||
delete[] m_pSvcDef->characteristics;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t numChrs = 0;
|
size_t numChrs = 0;
|
||||||
for (const auto& chr : m_vChars) {
|
for (const auto& chr : m_vChars) {
|
||||||
@@ -107,7 +96,7 @@ bool NimBLEService::start() {
|
|||||||
++numChrs;
|
++numChrs;
|
||||||
}
|
}
|
||||||
|
|
||||||
NIMBLE_LOGD(LOG_TAG, "Adding %d characteristics for service %s", numChrs, toString().c_str());
|
NIMBLE_LOGD(LOG_TAG, "Adding %zu characteristics for service %s", numChrs, getUUID().toString().c_str());
|
||||||
if (numChrs) {
|
if (numChrs) {
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
|
||||||
@@ -154,7 +143,7 @@ bool NimBLEService::start() {
|
|||||||
pChrs[i].arg = chr;
|
pChrs[i].arg = chr;
|
||||||
pChrs[i].flags = chr->getProperties();
|
pChrs[i].flags = chr->getProperties();
|
||||||
pChrs[i].min_key_size = 0;
|
pChrs[i].min_key_size = 0;
|
||||||
pChrs[i].val_handle = &chr->m_handle;
|
pChrs[i].val_handle = nullptr;
|
||||||
++i;
|
++i;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -162,15 +151,18 @@ bool NimBLEService::start() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
m_pSvcDef->type = BLE_GATT_SVC_TYPE_PRIMARY;
|
m_pSvcDef->type = BLE_GATT_SVC_TYPE_PRIMARY;
|
||||||
int rc = ble_gatts_count_cfg(m_pSvcDef);
|
|
||||||
|
int rc = ble_gatts_count_cfg(m_pSvcDef);
|
||||||
if (rc != 0) {
|
if (rc != 0) {
|
||||||
NIMBLE_LOGE(LOG_TAG, "ble_gatts_count_cfg failed, rc= %d, %s", rc, NimBLEUtils::returnCodeToString(rc));
|
NIMBLE_LOGE(LOG_TAG, "ble_gatts_count_cfg failed, rc= %d, %s", rc, NimBLEUtils::returnCodeToString(rc));
|
||||||
|
clearServiceDefinitions(); // Clear the definitions to free memory and reset the service for re-registration.
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = ble_gatts_add_svcs(m_pSvcDef);
|
rc = ble_gatts_add_svcs(m_pSvcDef);
|
||||||
if (rc != 0) {
|
if (rc != 0) {
|
||||||
NIMBLE_LOGE(LOG_TAG, "ble_gatts_add_svcs, rc= %d, %s", rc, NimBLEUtils::returnCodeToString(rc));
|
NIMBLE_LOGE(LOG_TAG, "ble_gatts_add_svcs, rc= %d, %s", rc, NimBLEUtils::returnCodeToString(rc));
|
||||||
|
clearServiceDefinitions(); // Clear the definitions to free memory and reset the service for re-registration.
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -178,6 +170,25 @@ bool NimBLEService::start() {
|
|||||||
return true;
|
return true;
|
||||||
} // start
|
} // start
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Clear the service definitions to free memory and reset the service for re-registration.
|
||||||
|
*/
|
||||||
|
void NimBLEService::clearServiceDefinitions() {
|
||||||
|
if (m_pSvcDef->characteristics) {
|
||||||
|
const ble_gatt_chr_def* chrDef = m_pSvcDef->characteristics;
|
||||||
|
while (chrDef->uuid != nullptr) {
|
||||||
|
if (chrDef->descriptors) {
|
||||||
|
delete[] chrDef->descriptors;
|
||||||
|
}
|
||||||
|
++chrDef;
|
||||||
|
}
|
||||||
|
delete[] m_pSvcDef->characteristics;
|
||||||
|
m_pSvcDef->characteristics = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_pSvcDef->type = 0; // Clear the type to indicate the service is not started/registered.
|
||||||
|
} // clearServiceDefinitions
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Create a new BLE Characteristic associated with this service.
|
* @brief Create a new BLE Characteristic associated with this service.
|
||||||
* @param [in] uuid - The UUID of the characteristic.
|
* @param [in] uuid - The UUID of the characteristic.
|
||||||
@@ -369,4 +380,4 @@ bool NimBLEService::isStarted() const {
|
|||||||
return m_pSvcDef->type > 0;
|
return m_pSvcDef->type > 0;
|
||||||
} // isStarted
|
} // isStarted
|
||||||
|
|
||||||
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL */
|
#endif // CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_PERIPHERAL)
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user