120 Commits

Author SHA1 Message Date
h2zero
0f5a184aa3 Add BLE stream classes. 2025-11-24 08:09:07 -07:00
h2zero
f216e95770 Add characteristic callbacks onStatus overload with conn info.
Adds a new overloaded callback to NimBLECharacteristicCallbacks for the notification/indication onStatus method that provides a NimBLEConnInfo reference.
2025-11-17 19:21:13 -07:00
h2zero
222f1590ed Refactor notify/indicate
This refactors the handling of sending notifications and indications for greater efficiency.
* Adds client subscription state tracking to NimBLECharacteristic rather than relying on the stack.
* Notifications/indications are now sent directly, no longer calling the callback to read the values.
  This avoids delays and flash writes in the stack, allowing for greater throughput.
2025-11-17 19:21:13 -07:00
srgg
149716a506 correct container byte size calculation to writeValue, notify, and indicate 2025-11-16 09:22:40 -07:00
srgg
4199c52af1 fix: correct byte size calculation for ATT values set from containers 2025-11-16 09:22:37 -07:00
h2zero
20158d62d0 [Bugfix] make sure the notify event is sent to server created clients 2025-10-24 13:19:42 -06:00
Quentin F
f6c8728ca3 Update 1.x_to2.x_migration_guide.md 2025-10-24 13:19:03 -06:00
h2zero
e0d3c4be39 Update workflows + add release publish 2025-10-24 13:09:46 -06:00
Guo-Rong
d163a9fdc6 Find client by handle during disconnect event.
If the peer has RPA enabled, searching by address fails due to address
resolution.
If this occurs, attempt to find the client by connection handle.
2025-10-23 11:55:24 -06:00
Chris Morgan
133c1a5da4 Usage_tips.md - Note that the library is threadsafe. 2025-10-23 11:54:37 -06:00
Chris Morgan
f622cdff0c README.md - Add a note about threadsafety 2025-10-23 11:54:37 -06:00
Chris Morgan
68068677ab Usage_tips.md - 'Device Local Name' information to help guide setting the GATT Device Name or Advertising name. 2025-09-28 19:31:04 -06:00
Chris Morgan
2c6ab706b3 Usage_tips.md - Detail persisted bonds limitations and considerations relative to CONFIG_BT_NIMBLE_MAX_CCCDS 2025-09-23 21:04:11 -06:00
h2zero
6f0b9ddf5d Convert NIMBLE_CPP macros to MYNEWT. 2025-09-06 16:59:55 -06:00
h2zero
8f9e85a46a Release 2.3.3 2025-09-05 16:11:11 -06:00
h2zero
7706f5a6b2 Support up to 1650 bytes of advertisement with extended advertising. 2025-09-05 15:44:47 -06:00
h2zero
1ffd013794 [Bugfix] Extended advertisements not reporting full data.
Extended advertisement reports would be truncated incorrectly as the handler was not checking the data status.

Correct advertisement length and set status on update.
2025-09-05 15:39:46 -06:00
h2zero
e8f7147ac5 [Bugfix] NimBLEAdvertisedDevice::isConnectable incorrect result 2025-09-05 15:11:38 -06:00
h2zero
6ee2a951f5 Release 2.3.2 2025-09-02 14:54:24 -06:00
h2zero
4b74939b6d Improve macros for code enablement 2025-09-02 14:36:02 -06:00
h2zero
9f7b9042e0 Fix docs build 2025-09-02 14:36:02 -06:00
h2zero
2cd5dc2aa2 Fix build with idf v5.5+ and specific roles are defined. 2025-08-25 09:11:13 -06:00
h2zero
9df8cc7dd1 Refactor to use MYNEWT_VAL macros.
This replaces the previously prefixed CONFIG_BT_X config macros with the underlying MYNEWT_VAL_X config macros that they affected.
2025-08-24 16:35:38 -06:00
iranl
88df909cfb Fix undefined reference to ble_svc_gap_device_name_set when GATT server is disabled (#349)
* Fix undefined reference to ble_svc_gap_device_name_set when GATT server is disabled

* Do not affect ESP-IDF <5.5.0
2025-08-24 16:07:59 -06:00
h2zero
8cefc0a562 [Bugfix] OnConnectfail not called when connection not established.
Workaround for when the disconnect event is sent when no connection has been established.
Espressif changed this from a connect event with error code to disconnect event.
2025-08-01 17:08:44 -06:00
h2zero
8af38e7eb9 Change default security settings to BLE secure connections off.
Fixing some connection issues when enabled, users should enable if desired.
2025-06-27 18:08:42 -06:00
h2zero
e7fead903c [Bugfix](workaround) OnConnect not being called.
Upstream changes have resulted in a possible status of BLE_ERR_UNSUPP_REM_FEATURE, this resulted in the onConnect callback not being called despite the connection actually being created.
This works around that bug to ensure that the connections are correctly tracked.
2025-06-27 18:07:46 -06:00
h2zero
a57c45e1de Fix build with idf versions < 5.x 2025-06-27 18:07:41 -06:00
h2zero
edfc838bef [Bugfix] allow peripheral and central roles without broadcast/scan. 2025-06-20 10:41:28 -06:00
h2zero
f1ead9959d Bump idf_component version 2025-06-11 11:40:42 -06:00
h2zero
b30421c19d Fix library.json version 2025-06-11 11:34:10 -06:00
h2zero
bdb868d125 Release 2.3.1 2025-06-11 11:14:52 -06:00
h2zero
503939c66f Update docs 2025-06-11 11:11:58 -06:00
Larry Davis
2640c44b45 Support passing data directly from NimBLEBeacon.getData() to NimBLEAdvertisementData.setManufacturerData() 2025-06-11 11:11:11 -06:00
h2zero
fec2d7a279 [Bugfix] NimBLEScan delete.
Calling NimBLEDevice::deint with the `clearAll` parameter set to `true` will delete the scan and any scan results but it was calling `clearall` which uses critical sections, this could cause a crash because the stack has already been de-initialized.
2025-06-09 17:13:30 -06:00
h2zero
e2cee2d994 Fix server client read/write not returning when encryption is used.
When the client created by the server reads or writes to an attribute and it triggers a pairing action the task will not be released because the client does not get the event.
This passes the event to the client to prevent the task from being hung.
2025-06-09 17:13:12 -06:00
h2zero
39f974625c Fix builds when exluding roles 2025-06-02 18:06:40 -06:00
John Boiles
169290f047 Allow esp_wifi_remote >= 0.5.3
`esp_wifi_remote` >= v0.10.0 is necessary to use esp-nimble-cpp with the latest ESP-IDF master branch.
2025-05-21 11:14:43 -06:00
h2zero
067274973d Release 2.3.0 2025-05-19 16:54:59 -06:00
h2zero
30877344bd Add support for esp32c5 2025-05-19 14:50:28 -06:00
h2zero
68dd0b37b5 [Bugfix] Attribute getValue fails with some data types 2025-05-19 14:28:53 -06:00
h2zero
e615deb1af Add Scan duplicate cache reset time feature.
Adds the ability to set a time for the scan duplicate filter cache to reset which will start reporting devices previously seen again.
2025-05-19 13:21:26 -06:00
h2zero
e29d1ce3cf Refactor L2Cap (style) 2025-05-05 18:30:37 -06:00
h2zero
a5702746c5 Add missed pointer parameter to const ref fix 2025-05-05 18:11:01 -06:00
h2zero
f8e8830112 [Bugfix] Incorrectly passing a pointers to a funtion taking const reference
Passing a pointer to to NimBLEUtils::taskWait and NimBLEUtils::taskRelease would compile due to the first argument in the constructor being
a pointer to void, a surprisingly not a compiler error in this instance. Also does not cause a crash but instead a hung task when called.
2025-05-05 18:06:34 -06:00
h2zero
80def6663b Fix preprocessor statement 2025-05-05 14:38:26 -06:00
h2zero
7a8d10bb71 Set max connections correctly for esp32s3/c3 2025-05-05 14:38:26 -06:00
h2zero
ce1b8bc2ec Fix build with non-esp devices 2025-05-05 14:38:07 -06:00
h2zero
dadbc0d423 [Bugfix] Correct NimBLEClient array initialization. 2025-05-03 15:00:43 -06:00
h2zero
da31f90436 Fix build when host based privacy is enabled without connection roles 2025-05-03 14:59:24 -06:00
Dr. Michael Lauer
e55ad9019c Introduce L2CAP infrastructure.
L2CAP is the underlying technology powering GATT.
BLE 5 exposes L2CAP COC (Connection Oriented Channels)
allowing a streaming API that leads to much higher throughputs
than you can achieve with updating GATT characteristics.

The patch follows the established infrastructure very closely.
The main components are:

- `NimBLEL2CAPChannel`, encapsulating an L2CAP COC.
- `NimBLEL2CAPServer`, encapsulating the L2CAP service.
- `Examples/L2CAP`, containing a client and a server application.

Apart from these, only minor adjustments to the existing code was
necessary.
2025-04-28 10:54:32 -06:00
h2zero
59e111ab55 Allow PHY updates without enabling extended advertising. 2025-04-25 09:07:23 -06:00
h2zero
e00dd88add [Bugfix] notify/indicate incorrectly returning success with custom value
When sending an array of data with a length value the wrong overload/template was being
used and the length value was being interpreted as the connection handle.

This updates the template to disable it when an array is passed and will now also report the error.
2025-04-24 17:08:19 -06:00
h2zero
e18d78678f [Bugfix] Explicit getValue template returning nil value with convertable types. 2025-04-24 17:07:42 -06:00
h2zero
54ec3f4c92 [Bugfix] Clear attribute value when zero length value is written. 2025-04-24 17:07:20 -06:00
thekurtovic
9ac9551a2b fix(Utils): Update gapEventToString switch case 2025-04-22 16:08:57 -06:00
h2zero
0172cb4b39 Only ignore scan discovery events when scan is stopped. 2025-04-14 08:52:32 -06:00
h2zero
856adebad5 Fix potential exception when scan is restarted.
If the scan is restarted in NimBLEScan::start, there is a chance that a result from
the previous scan arrives and the device is deleted when the callback is called.
This change ignores any results that come when the scan has stopped.
2025-04-14 08:39:28 -06:00
h2zero
033c7c27ad Fix extended server example - data too long error 2025-04-13 16:06:34 -06:00
h2zero
d134276d2d Bugfix - Characteristic onRead callback not called.
Fixes detection of the original read request vs a followup read for characteristic values greater than MTU - 3.
2025-04-13 15:13:53 -06:00
h2zero
01976cec54 [Bugfix] Incorrect onSubscribe value when indications are set. 2025-03-28 16:56:54 -06:00
Ryan Powell
723cdf0a66 Merge pull request #312 from iranl/patch-3
Fix ESP32-P4 compile when using Arduino as an ESP-IDF component
2025-03-10 18:30:44 -06:00
iranl
148087c508 Fix ESP32-P4 compile when using Arduino as an ESP-IDF component 2025-03-02 21:46:34 +01:00
h2zero
88c5d0c7b7 Bump docs version 2025-02-28 14:56:46 -07:00
h2zero
a6c03a2aaa Release 2.2.1 2025-02-28 09:51:31 -07:00
h2zero
9df8445241 [Bugfix] Check if remote descriptor vector has increased. 2025-02-25 17:08:01 -07:00
h2zero
e5f4d52a5a Update changelog 2025-02-25 14:26:57 -07:00
h2zero
7ef247c8db Partially revert Commit 052c0a04 to restore client connect with device parameter. 2025-02-25 14:23:25 -07:00
h2zero
c582c8c67e Release 2.2.0 2025-02-24 17:58:21 -07:00
William Emfinger
74b5c59887 fix(NimBLEDevice): fix crash when pairing table is full
* Add missing definition for default device callbacks (which prevents calling the `setDeviceCallbacks` method)
* Ensure `m_pDeviceCallbacks` inital value is set to `&defaultDeviceCallbacks` to prevent crash when pairing table is full

After #295 any time the pairing table fills up, the device will crash on the next pairing attempt.
2025-02-13 06:58:54 -07:00
h2zero
60efffdf2b [BugFix] Provide default task data when retrieving all descriptors.
* Update the descriptor filter when trying again with different UUID sizes.
2025-02-08 15:08:32 -07:00
thekurtovic
b6379848ae change(2904|AdvData): Add missing parameter name in declarations 2025-02-05 15:15:49 -07:00
thekurtovic
b29919df96 feat(Device): Implement store status handler 2025-02-05 15:14:42 -07:00
thekurtovic
052c0a0455 feat(AdvDevice): Add convenience operator to NimBLEAddress 2025-02-05 14:07:10 -07:00
thekurtovic
784e6f30fa Merge pull request #301 from thekurtovic/feat-log-condition
feat(Log): Add macros for conditional log print and rc handling
2025-01-28 14:59:53 -05:00
thekurtovic
5490cef129 feat(Log): Add macros for conditional log print and rc handling 2025-01-28 13:40:19 -05:00
h2zero
459e8c9fcd Release 2.1.1 2025-01-26 18:23:11 -07:00
thekurtovic
ffa8414747 refactor(RemoteChar): Reduce nesting
* Renamed desc_filter_t to NimBLEDescriptorFilter
* Added NimBLERemoteDescriptor pointer to NimBLEDescriptorFilter
* retrieveDescriptors changed to take NimBLEDescriptorFilter pointer
* General cleanup
2025-01-26 11:28:48 -07:00
h2zero
8130f88be8 Rename config macros to enable duplicate scan options on s3/c3 2025-01-26 11:09:36 -07:00
h2zero
c39e288c3e Workaround for P4 CI build error. 2025-01-26 09:41:25 -07:00
Guo-Rong
8158a16fbf Fix characteristic discovery with no descriptors.
Avoid discovery of descriptors if there are no handles remaining.
2025-01-15 18:37:47 -07:00
h2zero
57fe9cb77f Release 2.1.0 2025-01-12 19:17:10 -07:00
h2zero
e6249623d5 Fix race condition in NimBLEScan::clearResults.
If clear results is called from more than one task a race condition exists that may delete the same advertisedDevice twice.
This prevents this by swapping with an empty vector and testing for empty before freeing the resources.
2025-01-12 18:11:57 -07:00
Michael Ammann
a9578637cb avoid unused-variable warning
Avoid warning when compiled with All warnings enabled.
.../src/NimBLEScan.cpp: In member function 'void NimBLEScanResults::dump() const':
.../src/NimBLEScan.cpp:481:22: warning: unused variable 'dev' [-Wunused-variable]
  481 |     for (const auto& dev : m_deviceVec) {
      |                      ^~~
2025-01-12 15:44:58 -07:00
sanastasiou
765193f223 Fix incorrect TX power setting for advertising.
* Adds a new enum class to specify the tx power type to set (unused by non-esp devices).
* When all type is specified it is now ensured that all the tx power types are set to the specified level.
* Correctly sets the power level advertised for extended advertising with legacy pdu selected.
2025-01-12 15:37:44 -07:00
h2zero
3b019a0c9c If privacy is not configured do not share ID keys. 2025-01-12 15:05:11 -07:00
thekurtovic
5ac7272f5d change(NimBLEAddress): Prefer sizeof with parenthesis 2025-01-10 09:51:41 -07:00
h2zero
4d8ab46e56 Update new user guide 2025-01-10 09:11:49 -07:00
thekurtovic
8468bb50dc NimBLEAddress default to including delimiter in string format 2025-01-08 15:24:08 -07:00
thekurtovic
1088ad8fe1 NimBLEAddress::toString allow formatting to be configured 2025-01-07 11:37:01 -07:00
h2zero
0b543d9f62 Fix crash when retrieving descriptors.
If more than one descriptor is found the task would be released beofore the process completed causing a race condition.
2025-01-06 15:28:04 -07:00
h2zero
fa468d360a Release 2.0.3 2025-01-05 15:11:08 -07:00
h2zero
2759b1b69d Increase timeout paramter in server example. 2025-01-05 14:31:43 -07:00
h2zero
1a0b7a8e31 Add missing migration information to docs. 2025-01-05 14:24:28 -07:00
thekurtovic
1d01af0f8c NimBLEAdvertisementData emit error on failure, remove magic numbers. 2025-01-05 09:23:32 -07:00
h2zero
f7d3c5e0c2 Fix characteristic value unable to be updated in callback 2025-01-05 08:57:01 -07:00
h2zero
bc2bc35a8a Fix advertising data not set if scan response enabled later. 2025-01-05 08:56:57 -07:00
thekurtovic
15cb6bc7c6 Add checks in case NIMBLE_CPP_DEBUG_ASSERT is not defined. 2025-01-04 15:20:01 -07:00
afpineda
aae70dfd21 Allow the same report ID in multiple input/output/feature reports 2025-01-04 15:16:01 -07:00
h2zero
ad12a48e9e Fix build error when using platformio library 2025-01-02 08:29:44 -07:00
thekurtovic
09297fb72a NimBLEScan increment m_callbackSent before callback.
Fixes heap corruption observed on CONFIG_HEAP_POISONING_COMPREHENSIVE.
2025-01-02 08:29:17 -07:00
h2zero
b50a1b50b1 Release 2.0.2 2024-12-21 17:04:48 -07:00
h2zero
954ba0b5a7 Update Changelog 2024-12-21 16:05:38 -07:00
h2zero
015217c764 Fix compile error for advertiser only config. 2024-12-21 15:47:13 -07:00
thekurtovic
9a1ae677b8 NimBLELog allow custom log level colors. 2024-12-20 13:32:34 -07:00
afpineda
6ee5174ba0 Fix NimBLEHIDDevice not being able to create more than one in/out/feature report + temporal coupling 2024-12-20 13:29:50 -07:00
William Emfinger
e843c6811c Fix crash that could occur when server reconnects
* Update to not change services if they have not actually changed when starting the server
Closes #270
2024-12-20 13:25:00 -07:00
h2zero
0d13044703 Release 2.0.1 2024-12-16 18:35:21 -07:00
h2zero
cfca3e7500 Add missing NimBLEUtils and NimBLEConnInfo includes to NimBLEDevice.h
In some cases compilation of examples would fail due to missing these headers so they should be included in NimBLEDevice.h
2024-12-15 14:15:16 -07:00
h2zero
3c80c950ae Update changelog. 2024-12-15 10:06:39 -07:00
Ryan Powell
182e3bb8a3 Fix NimBLEHIDDevice output report returning incorrect characteristic. (#805)
The input and output report characteristics share the same UUID and the `getOutputReport` was returning the input report characteristic.
This change will return the correct characteristic by finding it with the index parameter.
2024-12-15 10:02:47 -07:00
mr258876
a627007a31 Fix compiling errors when central is disabled.
* Fix missing member error 'm_pClient' in NimBLEServer
2024-12-15 09:35:28 -07:00
h2zero
a6ed1933bc Release 2.0.0 2024-12-14 16:25:52 -07:00
h2zero
675d6bbf0d [BREAKING] Remove NimBLEAddress type default value.
When constructing a NimBLEAddress via string or other non `ble_addr_t` parameters it is important that the address type be specified.
This will help prevent issues where applications are not able to connect or identify scanned devices when comparing their addresses.
2024-12-14 13:56:50 -07:00
h2zero
8753782425 Fix example 2024-12-13 21:25:27 -07:00
h2zero
af1872623e Silence compiler warnings 2024-12-13 15:25:07 -07:00
h2zero
62de1b23fa Add library.json
* Add fields for idf registry
2024-12-13 12:06:45 -07:00
h2zero
57e583c17a [BREAKING] Refactor NimBLELog
* Cleanup style.
* Remove NIMBLE_LOGC as it's use was limited to assertions.
* Add delay after assert printf to allow time to display the message.
2024-12-13 12:06:07 -07:00
h2zero
5afb5af81e Update license 2024-12-12 20:41:04 -07:00
h2zero
2d1e33a75e Prepare for v2 release 2024-12-12 08:46:42 -07:00
102 changed files with 4428 additions and 1752 deletions

View File

@@ -17,8 +17,8 @@ jobs:
# See https://hub.docker.com/r/espressif/idf/tags and
# https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/tools/idf-docker-image.html
# for details.
idf_ver: ["release-v4.4", "release-v5.1", "release-v5.3"]
idf_target: ["esp32", "esp32s3", "esp32c2", "esp32c3", "esp32c6", "esp32h2", "esp32p4"]
idf_ver: ["release-v4.4", "release-v5.4", "release-v5.5"]
idf_target: ["esp32", "esp32s3", "esp32c2", "esp32c3", "esp32c5", "esp32c6", "esp32h2", "esp32p4"]
example:
- NimBLE_Client
- NimBLE_Server
@@ -31,6 +31,8 @@ jobs:
example: Bluetooth_5/NimBLE_extended_server
- idf_ver: release-v4.4
idf_target: "esp32c2"
- idf_ver: release-v4.4
idf_target: "esp32c5"
- idf_ver: release-v4.4
idf_target: "esp32c6"
- idf_ver: release-v4.4
@@ -43,7 +45,7 @@ jobs:
container: espressif/idf:${{ matrix.idf_ver }}
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
path: components/esp-nimble-cpp
- name: Build examples

43
.github/workflows/release.yml vendored Normal file
View File

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

17
.github/workflows/sponsors.yml vendored Normal file
View File

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

3
.gitignore vendored
View File

@@ -1 +1,2 @@
docs/doxydocs
docs/doxydocs
dist

View File

@@ -1,7 +1,133 @@
# Changelog
All notable changes to this project will be documented in this file.
## [Unreleased]
## [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
## **Breaking changes**
- All connection oriented callbacks now receive a reference to `NimBLEConnInfo`, the `ble_gap_conn_desc` has also been replace with `NimBLEConnInfo` in the functions that received it.
@@ -60,6 +186,7 @@ All notable changes to this project will be documented in this file.
- `NimBLEService::getCharacteristics` now returns a `const std::vector<NimBLECharacteristic*>&` instead of std::vector<NimBLECharacteristic *>.
- `NimBLEUUID::getNative` method replaced with `NimBLEUUID::getBase` which returns a read-only pointer to the underlying `ble_uuid_t` struct.
- `NimBLEUUID`; `msbFirst` parameter has been removed from constructor, caller should reverse the data first or call the new `reverseByteOrder` method after.
- `NimBLEAddress` constructor; default value for the `type` parameter removed, caller should know the address type and specify it.
- `NimBLEAddress::getNative` replaced with `NimBLEAddress::getBase` and now returns a pointer to `const ble_addr_t` instead of a pointer to the address value.
- `NimBLEAddress::equals` method and `NimBLEAddress::== operator` will now also test if the address types are the same.
- `NimBLEUtils::dumpGapEvent` function removed.

View File

@@ -35,6 +35,7 @@ idf_component_register(
"esp32s3"
"esp32c2"
"esp32c3"
"esp32c5"
"esp32c6"
"esp32h2"
"esp32p4"
@@ -55,6 +56,8 @@ idf_component_register(
"src/NimBLEEddystoneTLM.cpp"
"src/NimBLEExtAdvertising.cpp"
"src/NimBLEHIDDevice.cpp"
"src/NimBLEL2CAPChannel.cpp"
"src/NimBLEL2CAPServer.cpp"
"src/NimBLERemoteCharacteristic.cpp"
"src/NimBLERemoteDescriptor.cpp"
"src/NimBLERemoteService.cpp"

View File

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

131
Kconfig
View File

@@ -26,6 +26,113 @@ config NIMBLE_CPP_LOG_LEVEL
default 3 if NIMBLE_CPP_LOG_LEVEL_INFO
default 4 if NIMBLE_CPP_LOG_LEVEL_DEBUG
config NIMBLE_CPP_LOG_OVERRIDE_COLOR
bool "Enable log color override."
default "n"
help
Enabling this option will allow NimBLE log levels to have
specific colors assigned.
menu "NIMBLE Log Override Colors"
depends on NIMBLE_CPP_LOG_OVERRIDE_COLOR
choice NIMBLE_CPP_LOG_OVERRIDE_COLOR_ERR
prompt "NimBLE CPP log override color Error"
default NIMBLE_CPP_LOG_OVERRIDE_COLOR_ERR_NONE
help
Select NimBLE CPP log override error color.
config NIMBLE_CPP_LOG_OVERRIDE_COLOR_ERR_NONE
bool "None"
config NIMBLE_CPP_LOG_OVERRIDE_COLOR_ERR_BLACK
bool "Black"
config NIMBLE_CPP_LOG_OVERRIDE_COLOR_ERR_RED
bool "Red"
config NIMBLE_CPP_LOG_OVERRIDE_COLOR_ERR_GREEN
bool "Green"
config NIMBLE_CPP_LOG_OVERRIDE_COLOR_ERR_YELLOW
bool "Yellow"
config NIMBLE_CPP_LOG_OVERRIDE_COLOR_ERR_BLUE
bool "Blue"
config NIMBLE_CPP_LOG_OVERRIDE_COLOR_ERR_PURPLE
bool "Purple"
config NIMBLE_CPP_LOG_OVERRIDE_COLOR_ERR_CYAN
bool "Cyan"
endchoice #NIMBLE_CPP_LOG_OVERRIDE_COLOR_ERR
choice NIMBLE_CPP_LOG_OVERRIDE_COLOR_WARN
prompt "NimBLE CPP log override color Warning"
default NIMBLE_CPP_LOG_OVERRIDE_COLOR_WARN_NONE
help
Select NimBLE CPP log override warning color.
config NIMBLE_CPP_LOG_OVERRIDE_COLOR_WARN_NONE
bool "None"
config NIMBLE_CPP_LOG_OVERRIDE_COLOR_WARN_BLACK
bool "Black"
config NIMBLE_CPP_LOG_OVERRIDE_COLOR_WARN_RED
bool "Red"
config NIMBLE_CPP_LOG_OVERRIDE_COLOR_WARN_GREEN
bool "Green"
config NIMBLE_CPP_LOG_OVERRIDE_COLOR_WARN_YELLOW
bool "Yellow"
config NIMBLE_CPP_LOG_OVERRIDE_COLOR_WARN_BLUE
bool "Blue"
config NIMBLE_CPP_LOG_OVERRIDE_COLOR_WARN_PURPLE
bool "Purple"
config NIMBLE_CPP_LOG_OVERRIDE_COLOR_WARN_CYAN
bool "Cyan"
endchoice #NIMBLE_CPP_LOG_OVERRIDE_COLOR_WARN
choice NIMBLE_CPP_LOG_OVERRIDE_COLOR_INFO
prompt "NimBLE CPP log override color Info"
default NIMBLE_CPP_LOG_OVERRIDE_COLOR_INFO_NONE
help
Select NimBLE CPP log override info color.
config NIMBLE_CPP_LOG_OVERRIDE_COLOR_INFO_NONE
bool "None"
config NIMBLE_CPP_LOG_OVERRIDE_COLOR_INFO_BLACK
bool "Black"
config NIMBLE_CPP_LOG_OVERRIDE_COLOR_INFO_RED
bool "Red"
config NIMBLE_CPP_LOG_OVERRIDE_COLOR_INFO_GREEN
bool "Green"
config NIMBLE_CPP_LOG_OVERRIDE_COLOR_INFO_YELLOW
bool "Yellow"
config NIMBLE_CPP_LOG_OVERRIDE_COLOR_INFO_BLUE
bool "Blue"
config NIMBLE_CPP_LOG_OVERRIDE_COLOR_INFO_PURPLE
bool "Purple"
config NIMBLE_CPP_LOG_OVERRIDE_COLOR_INFO_CYAN
bool "Cyan"
endchoice #NIMBLE_CPP_LOG_OVERRIDE_COLOR_INFO
choice NIMBLE_CPP_LOG_OVERRIDE_COLOR_DEBUG
prompt "NimBLE CPP log override color Debug"
default NIMBLE_CPP_LOG_OVERRIDE_COLOR_DEBUG_NONE
help
Select NimBLE CPP log override debug color.
config NIMBLE_CPP_LOG_OVERRIDE_COLOR_DEBUG_NONE
bool "None"
config NIMBLE_CPP_LOG_OVERRIDE_COLOR_DEBUG_BLACK
bool "Black"
config NIMBLE_CPP_LOG_OVERRIDE_COLOR_DEBUG_RED
bool "Red"
config NIMBLE_CPP_LOG_OVERRIDE_COLOR_DEBUG_GREEN
bool "Green"
config NIMBLE_CPP_LOG_OVERRIDE_COLOR_DEBUG_YELLOW
bool "Yellow"
config NIMBLE_CPP_LOG_OVERRIDE_COLOR_DEBUG_BLUE
bool "Blue"
config NIMBLE_CPP_LOG_OVERRIDE_COLOR_DEBUG_PURPLE
bool "Purple"
config NIMBLE_CPP_LOG_OVERRIDE_COLOR_DEBUG_CYAN
bool "Cyan"
endchoice #NIMBLE_CPP_LOG_OVERRIDE_COLOR_DEBUG
endmenu
config NIMBLE_CPP_ENABLE_RETURN_CODE_TEXT
bool "Show NimBLE return codes as text in debug log."
default "n"
@@ -50,6 +157,20 @@ config NIMBLE_CPP_ENABLE_ADVERTISEMENT_TYPE_TEXT
while scanning as text messages in the debug log.
This will use approximately 250 bytes of flash memory.
config NIMBLE_CPP_ADDR_FMT_EXCLUDE_DELIMITER
bool "Exclude colon characters when printing address."
default "n"
help
Enabling this option will format MAC addresses without
colon characters when printing.
config NIMBLE_CPP_ADDR_FMT_UPPERCASE
bool "Use uppercase letters when printing address."
default "n"
help
Enabling this option will format MAC addresses in
uppercase letters when printing.
config NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED
bool "Enable timestamps to be stored with attribute values."
default "n"
@@ -77,12 +198,16 @@ config NIMBLE_CPP_DEBUG_ASSERT_ENABLED
This will use approximately 1kB of flash memory.
config NIMBLE_CPP_FREERTOS_TASK_BLOCK_BIT
int "FreeRTOS task block bit"
int "FreeRTOS task block bit."
default 31
help
Configure the bit to set in the task notification value when a task is blocked waiting for an event.
This should be set to a bit that is not used by other notifications in the system.
config NIMBLE_CPP_IDF
bool
default BT_NIMBLE_ENABLED
#
# BT config
#
@@ -130,4 +255,4 @@ if IDF_TARGET_ESP32P4
endchoice
endif
endmenu
endmenu

View File

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

10
NOTICE Normal file
View File

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

View File

@@ -1,19 +1,22 @@
[Latest release ![Release Version](https://img.shields.io/github/release/h2zero/esp-nimble-cpp.svg?style=plastic)
[![Release Version](https://img.shields.io/github/release/h2zero/esp-nimble-cpp.svg?style=plastic)
![Release Date](https://img.shields.io/github/release-date/h2zero/esp-nimble-cpp.svg?style=plastic)](https://github.com/h2zero/esp-nimble-cpp/releases/latest/)
Need help? Have questions or suggestions? Join the [![Gitter](https://badges.gitter.im/NimBLE-Arduino/community.svg)](https://gitter.im/NimBLE-Arduino/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
<br/>
> [!IMPORTANT]
> Version 2 is now released!
> Check out the [1.x to 2.x Migration Guide](docs/1.x_to2.x_migration_guide.md) and [Release Notes](https://github.com/h2zero/esp-nimble-cpp/releases/latest/)
# esp-nimble-cpp
NimBLE CPP library for use with ESP32 that attempts to maintain compatibility with the [nkolban cpp_uitls BLE API](https://github.com/nkolban/esp32-snippets/tree/master/cpp_utils).
NimBLE CPP library for use with ESP32 that attempts to maintain compatibility with the [nkolban cpp_utils BLE API](https://github.com/nkolban/esp32-snippets/tree/master/cpp_utils).
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)**
This library **significantly** reduces resource usage and improves performance for ESP32 BLE applications as compared
with the bluedroid based library. The goal is to maintain, as much as reasonable, compatibility with the original
library but refactored to use the NimBLE stack. In addition, this library will be more actively developed and maintained
to provide improved capabilites and stability over the original.
library but using the NimBLE stack. In addition, this library will be more actively developed and maintained
to provide improved capabilities and stability over the original.
**Testing shows a nearly 50% reduction in flash use and approx. 100kB less ram consumed vs the original!**
*Your results may vary*
@@ -35,16 +38,6 @@ Configure settings in `NimBLE Options`.
Call `NimBLEDevice::init("");` in `app_main`.
<br/>
### ESP-IDF v3.2 & v3.3
The NimBLE component does not come with these versions of IDF (now included in 3.3.2 and above).
A backport that works in these versions has been created and is [available here](https://github.com/h2zero/esp-nimble-component).
Download or clone that repo into your project/components folder and run menuconfig.
Configure settings in `main menu -> NimBLE Options`.
`#include "NimBLEDevice.h"` in main.cpp.
Call `NimBLEDevice::init("");` in `app_main`.
<br/>
# Using
This library is intended to be compatible with the original ESP32 BLE functions and types with minor changes.
@@ -62,6 +55,14 @@ When using this library along with Arduino and compiling with *CMake* you must a
in your project/CMakeLists.txt after the line `include($ENV{IDF_PATH}/tools/cmake/project.cmake)` to prevent Arduino from releasing BLE memory.
<br>
# Sponsors
Thank you to all the sponsors who support this project!
<!-- sponsors --><!-- sponsors -->
If you use this library for a commercial product please consider [sponsoring the development](https://github.com/sponsors/h2zero) to ensure the continued updates and maintenance.
<br/>
# Acknowledgments
* [nkolban](https://github.com/nkolban) and [chegewara](https://github.com/chegewara) for the [original esp32 BLE library](https://github.com/nkolban/esp32-snippets/tree/master/cpp_utils) this project was derived from.
* [beegee-tokyo](https://github.com/beegee-tokyo) for contributing your time to test/debug and contributing the beacon examples.

View File

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

View File

@@ -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`.
- `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::getClientByID(uint16_t conn_id);` has been changed to `NimBLEDevice::getClientByHandle(uint16_t connHandle)`
<br/>
## 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)`
`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/>
## BLE UUID's
@@ -65,7 +66,8 @@ This returns a pointer to `const ble_addr_t` instead of a pointer to the address
## Server
- `NimBLEServer::disconnect` now returns `bool`, true = success, instead of `int` to be consistent with the rest of the library.
- `NimBLEServerCallbacks::onMTUChanged` renamed to `NimBLEServerCallbacks::onMTUChange` to be consistent with the client callback.
- `NimBLEServer::getPeerIDInfo` renamed to `NimBLEServer::getPeerInfoByHandle` to better describe it's use.
- `NimBLEServer::getPeerIDInfo` renamed to `NimBLEServer::getPeerInfoByHandle` to better describe it's use.
- Advertising is no longer automatically restarted when a peer disconnects, to re-enable this feature either call `NimBLEServer::advertiseOnDisconnect(true);` after creating the server or manually restart advertising in the `onDisconnect` callback.
<br/>
### Services
@@ -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::onScanEnd`, replaces the scanEnded callback passed to `NimBLEScan::start` and now takes a `const NimBLEScanResults&` and `int reason` parameter.
- - `NimBLEScanCallbacks::onDiscovered`, This is called immediately when a device is first scanned, before any scan response data is available and takes a `const NimBLEAdvertisedDevice*` parameter.
- `NimBLEScan::setAdvertisedDeviceCallbacks(NimBLEAdvertisedDeviceCallbacks* callbacks, bool wantDuplicates)` has been changed to `NimBLEScan::setScanCallbacks(NimBLEScanCallbacks* callbacks, bool wantDuplicates);`
<br/>
### Advertised Device

View File

@@ -48,8 +48,7 @@ PROJECT_NAME = esp-nimble-cpp
# could be handy for archiving the generated documentation or if some version
# control system is used.
PROJECT_NUMBER = 2.0.0
PROJECT_NUMBER = 2.3.3
# 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
# 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.
PREDEFINED = _DOXYGEN_ \
CONFIG_BT_ENABLED \
CONFIG_BT_NIMBLE_ROLE_CENTRAL \
CONFIG_BT_NIMBLE_ROLE_OBSERVER \
CONFIG_BT_NIMBLE_ROLE_PERIPHERAL \
CONFIG_BT_NIMBLE_ROLE_BROADCASTER \
CONFIG_BT_NIMBLE_EXT_ADV
CONFIG_BT_NIMBLE_ENABLED=1 \
MYNEWT_VAL_BLE_ROLE_CENTRAL=1 \
MYNEWT_VAL_BLE_ROLE_OBSERVER=1 \
MYNEWT_VAL_BLE_ROLE_PERIPHERAL=1 \
MYNEWT_VAL_BLE_ROLE_BROADCASTER=1 \
MYNEWT_VAL_BLE_EXT_ADV=1
# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
# tag can be used to specify a list of macro names that should be expanded. The

View File

@@ -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:
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/>
> BLEAdvertising::start (NimBLEAdvertising::start)

View File

@@ -37,9 +37,7 @@ For this example we will keep it simple and use a 16 bit value: ABCD.
```
#include "NimBLEDevice.h"
// void setup() in Arduino
void app_main(void)
{
extern "C" void app_main(void) {
NimBLEDevice::init("NimBLE");
NimBLEServer *pServer = NimBLEDevice::createServer();
@@ -79,9 +77,7 @@ The function call will simply be `pService->createCharacteristic("1234");`
```
#include "NimBLEDevice.h"
// void setup() in Arduino
void app_main(void)
{
extern "C" void app_main(void) {
NimBLEDevice::init("NimBLE");
NimBLEServer *pServer = NimBLEDevice::createServer();
@@ -99,12 +95,13 @@ There are many different types you can send as parameters for the value but for
`pCharacteristic->setValue("Hello BLE");`
Next we need to advertise for connections.
To do this we create an instance of `NimBLEAdvertising` add our service to it (optional) and start advertisng.
To do this we create an instance of `NimBLEAdvertising` add our service to it (optional) and start advertising.
**The code for this will be:**
```
NimBLEAdvertising *pAdvertising = NimBLEDevice::getAdvertising(); // create advertising instance
pAdvertising->addServiceUUID("ABCD"); // tell advertising the UUID of our service
pAdvertising->addServiceUUID("ABCD"); // advertise the UUID of our service
pAdvertising->setName("NimBLE"); // advertise the device name
pAdvertising->start(); // start advertising
```
That's it, this will be enough to create a BLE server with a service and a characteristic and advertise for client connections.
@@ -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"
// void setup() in Arduino
void app_main(void)
{
extern "C" void app_main(void) {
NimBLEDevice::init("NimBLE");
NimBLEServer *pServer = NimBLEDevice::createServer();
@@ -126,7 +121,8 @@ void app_main(void)
pCharacteristic->setValue("Hello BLE");
NimBLEAdvertising *pAdvertising = NimBLEDevice::getAdvertising();
pAdvertising->addServiceUUID("ABCD");
pAdvertising->addServiceUUID("ABCD"); // advertise the UUID of our service
pAdvertising->setName("NimBLE"); // advertise the device name
pAdvertising->start();
}
```
@@ -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.
To do this we call `NimBLEScan::start(duration)`, the duration parameter is a uint32_t that specifies the number of milliseconds to scan for,
To do this we call `NimBLEScan::getResults(duration)`, the duration parameter is a uint32_t that specifies the number of milliseconds to scan for,
passing 0 will scan forever.
In this example we will scan for 10 seconds. This is a blocking function (a non blocking overload is also available).
@@ -154,9 +150,7 @@ This call returns an instance of `NimBLEScanResults` when the scan completes whi
```
#include "NimBLEDevice.h"
// void setup() in Arduino
void app_main(void)
{
extern "C" void app_main(void) {
NimBLEDevice::init("");
NimBLEScan *pScan = NimBLEDevice::getScan();
@@ -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.
To do this we iterate through the results and check if any of the devices found are advertising the service we want `ABCD`.
Each result in `NimBLEScanResults` is a `NimBLEAdvertisedDevice` instance that we can access data from.
Each result in `NimBLEScanResults` is a `const NimBLEAdvertisedDevice*` that we can access data from.
We will check each device found for the `ABCD` service by calling `NimBLEAdvertisedDevice::isAdvertisingService`.
This takes an instance of `NimBLEUUID` as a parameter so we will need to create one.
@@ -177,11 +171,11 @@ This takes an instance of `NimBLEUUID` as a parameter so we will need to create
```
NimBLEUUID serviceUuid("ABCD");
for(int i = 0; i < results.getCount(); i++) {
NimBLEAdvertisedDevice device = results.getDevice(i);
for (int i = 0; i < results.getCount(); i++) {
const NimBLEAdvertisedDevice *device = results.getDevice(i);
if (device.isAdvertisingService(serviceUuid)) {
// create a client and connect
if (device->isAdvertisingService(serviceUuid)) {
// create a client and connect
}
}
```
@@ -198,16 +192,16 @@ This takes a pointer to the `NimBLEAdvertisedDevice` and returns `true` if succe
```
NimBLEUUID serviceUuid("ABCD");
for(int i = 0; i < results.getCount(); i++) {
NimBLEAdvertisedDevice device = results.getDevice(i);
for (int i = 0; i < results.getCount(); i++) {
const NimBLEAdvertisedDevice *device = results.getDevice(i);
if (device.isAdvertisingService(serviceUuid)) {
if (device->isAdvertisingService(serviceUuid)) {
NimBLEClient *pClient = NimBLEDevice::createClient();
if(pClient->connect(&device)) {
//success
if (pClient->connect(&device)) {
//success
} else {
// failed to connect
// failed to connect
}
}
}
@@ -229,11 +223,15 @@ Finally we will read the characteristic value with `NimBLERemoteCharacteristic::
```
NimBLEUUID serviceUuid("ABCD");
for(int i = 0; i < results.getCount(); i++) {
NimBLEAdvertisedDevice device = results.getDevice(i);
for (int i = 0; i < results.getCount(); i++) {
const NimBLEAdvertisedDevice *device = results.getDevice(i);
if (device.isAdvertisingService(serviceUuid)) {
if (device->isAdvertisingService(serviceUuid)) {
NimBLEClient *pClient = NimBLEDevice::createClient();
if (!pClient) { // Make sure the client was created
break;
}
if (pClient->connect(&device)) {
NimBLERemoteService *pService = pClient->getService(serviceUuid);
@@ -247,7 +245,7 @@ for(int i = 0; i < results.getCount(); i++) {
}
}
} else {
// failed to connect
// failed to connect
}
}
}
@@ -262,12 +260,16 @@ This is done by calling `NimBLEDevice::deleteClient`.
```
NimBLEUUID serviceUuid("ABCD");
for(int i = 0; i < results.getCount(); i++) {
NimBLEAdvertisedDevice device = results.getDevice(i);
for (int i = 0; i < results.getCount(); i++) {
const NimBLEAdvertisedDevice *device = results.getDevice(i);
if (device.isAdvertisingService(serviceUuid)) {
if (device->isAdvertisingService(serviceUuid)) {
NimBLEClient *pClient = NimBLEDevice::createClient();
if (!pClient) { // Make sure the client was created
break;
}
if (pClient->connect(&device)) {
NimBLERemoteService *pService = pClient->getService(serviceUuid);
@@ -280,7 +282,7 @@ for(int i = 0; i < results.getCount(); i++) {
}
}
} else {
// failed to connect
// failed to connect
}
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"
// void setup() in Arduino
void app_main(void)
{
extern "C" void app_main(void) {
NimBLEDevice::init("");
NimBLEScan *pScan = NimBLEDevice::getScan();
NimBLEScanResults results = pScan->start(10 * 1000);
NimBLEScanResults results = pScan->getResults(10 * 1000);
NimBLEUUID serviceUuid("ABCD");
for(int i = 0; i < results.getCount(); i++) {
NimBLEAdvertisedDevice device = results.getDevice(i);
if (device.isAdvertisingService(serviceUuid)) {
for (int i = 0; i < results.getCount(); i++) {
const NimBLEAdvertisedDevice *device = results.getDevice(i);
if (device->isAdvertisingService(serviceUuid)) {
NimBLEClient *pClient = NimBLEDevice::createClient();
if (!pClient) { // Make sure the client was created
break;
}
if (pClient->connect(&device)) {
NimBLERemoteService *pService = pClient->getService(serviceUuid);
if (pService != nullptr) {
NimBLERemoteCharacteristic *pCharacteristic = pService->getCharacteristic("1234");
if (pCharacteristic != nullptr) {
std::string value = pCharacteristic->readValue();
// print or do whatever you need with the value
}
}
} else {
// failed to connect
// failed to connect
}
NimBLEDevice::deleteClient(pClient);
}
}
@@ -334,4 +338,3 @@ void app_main(void)
For more advanced features and options please see the client examples in the examples folder.
<br/>

View File

@@ -1,11 +1,8 @@
# 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 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/>
This library is threadsafe. Attribues can be manipulated freely.
## 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.
<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
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.

View File

@@ -1,5 +1,4 @@
# Overview
This is a C++ BLE library for the ESP32 that uses the NimBLE host stack instead of bluedroid.
The aim is to maintain, as much as reasonable, the original bluedroid C++ & Arduino BLE API by while adding new features
and making improvements in performance, resource use, and stability.
@@ -23,16 +22,6 @@ Configure settings in `NimBLE Options`.
Call `NimBLEDevice::init` in `app_main`.
<br/>
### v3.2 & v3.3
The NimBLE component does not come with these versions of IDF (now included in 3.3.2 and above).
A backport that works in these versions has been created and is [available here](https://github.com/h2zero/esp-nimble-component).
Download or clone that repo into your project/components folder and run menuconfig.
Configure settings in `main menu -> NimBLE Options`.
`#include "NimBLEDevice.h"` in main.cpp.
Call `NimBLEDevice::init` in `app_main`.
<br/>
# Using
This library is intended to be compatible with the original ESP32 BLE functions and types with minor changes.
@@ -43,12 +32,13 @@ If you are familiar with the original library, see: [The migration guide](Migrat
For more advanced usage see [Usage tips](Usage_tips.md) for more performance and optimization.
<br/>
# Need help? Have a question or suggestion?
Come chat on [gitter](https://gitter.im/NimBLE-Arduino/community?utm_source=share-link&utm_medium=link&utm_campaign=share-link) or open an issue at [NimBLE-Arduino](https://github.com/h2zero/NimBLE-Arduino/issues) or [esp-nimble-cpp](https://github.com/h2zero/esp-nimble-cpp/issues)
# Sponsors
Thank you to all the sponsors who support this project!
If you use this library for a commercial product please consider [sponsoring the development](https://github.com/sponsors/h2zero) to ensure the continued updates and maintenance.
<br/>
# Acknowledgments
* [nkolban](https://github.com/nkolban) and [chegewara](https://github.com/chegewara) for the [original esp32 BLE library](https://github.com/nkolban/esp32-snippets/tree/master/cpp_utils) this project was derived from.
* [beegee-tokyo](https://github.com/beegee-tokyo) for contributing your time to test/debug and contributing the beacon examples.
* [Jeroen88](https://github.com/Jeroen88) for the amazing help debugging and improving the client code.

View File

@@ -14,8 +14,8 @@
#define CHARACTERISTIC_UUID "1234"
static const NimBLEAdvertisedDevice* advDevice;
static bool doConnect = false;
static uint32_t scanTime = 10 * 1000; // In milliseconds, 0 = scan forever
static bool doConnect = false;
static uint32_t scanTimeMs = 10 * 1000; // In milliseconds, 0 = scan forever
/** Define the PHY's to use when connecting to peer devices, can be 1, 2, or all 3 (default).*/
static uint8_t connectPhys = BLE_GAP_LE_PHY_CODED_MASK | BLE_GAP_LE_PHY_1M_MASK /*| BLE_GAP_LE_PHY_2M_MASK */;
@@ -26,7 +26,7 @@ class ClientCallbacks : public NimBLEClientCallbacks {
void onDisconnect(NimBLEClient* pClient, int reason) override {
printf("%s Disconnected, reason = %d - Starting scan\n", pClient->getPeerAddress().toString().c_str(), reason);
NimBLEDevice::getScan()->start(scanTime);
NimBLEDevice::getScan()->start(scanTimeMs);
}
} clientCallbacks;
@@ -123,7 +123,7 @@ extern "C" void app_main(void) {
* Start scanning for advertisers for the scan time specified (in milliseconds) 0 = forever
* Optional callback for when scanning stops.
*/
pScan->start(scanTime);
pScan->start(scanTimeMs);
printf("Scanning for peripherals\n");
@@ -137,7 +137,7 @@ extern "C" void app_main(void) {
}
doConnect = false;
NimBLEDevice::getScan()->start(scanTime);
NimBLEDevice::getScan()->start(scanTimeMs);
}
vTaskDelay(pdMS_TO_TICKS(10));
}

View File

@@ -9,8 +9,8 @@
#include <NimBLEDevice.h>
static uint32_t scanTime = 10 * 1000; // In milliseconds, 0 = scan forever
static NimBLEScan::Phy scanPhy = NimBLEScan::Phy::SCAN_ALL;
static uint32_t scanTimeMs = 10 * 1000; // In milliseconds, 0 = scan forever
static NimBLEScan::Phy scanPhy = NimBLEScan::Phy::SCAN_ALL;
/** Define a class to handle the callbacks when advertisements are received */
class ScanCallbacks : public NimBLEScanCallbacks {
@@ -43,7 +43,7 @@ class ScanCallbacks : public NimBLEScanCallbacks {
NimBLEScan* pScan = NimBLEDevice::getScan();
pScan->setPhy(scanPhy);
pScan->start(scanTime);
pScan->start(scanTimeMs);
}
} scanCallbacks;
@@ -63,7 +63,7 @@ extern "C" void app_main(void) {
/** Set the initial PHY's to scan on, default is SCAN_ALL */
pScan->setPhy(scanPhy);
/** Start scanning for scanTime */
pScan->start(scanTime);
/** Start scanning for scanTimeMs */
pScan->start(scanTimeMs);
printf("Scanning for peripherals\n");
}

View File

@@ -65,8 +65,7 @@ class AdvertisingCallbacks : public NimBLEExtAdvertisingCallbacks {
}
} advertisingCallbacks;
extern "C"
void app_main(void) {
extern "C" void app_main(void) {
/** Initialize NimBLE and set the device name */
NimBLEDevice::init("Extended advertiser");
@@ -105,7 +104,6 @@ void app_main(void) {
"This example message is 226 bytes long "
"and is using CODED_PHY for long range."));
extAdv.setCompleteServices16({NimBLEUUID(SERVICE_UUID)});
extAdv.setName("Extended advertiser");
/** When extended advertising is enabled `NimBLEDevice::getAdvertising` returns a pointer to `NimBLEExtAdvertising */

5
examples/L2CAP/.gitignore vendored Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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

View File

@@ -10,8 +10,8 @@
#include <NimBLEDevice.h>
static const NimBLEAdvertisedDevice* advDevice;
static bool doConnect = false;
static uint32_t scanTime = 5000; /** scan time in milliseconds, 0 = scan forever */
static bool doConnect = false;
static uint32_t scanTimeMs = 5000; /** scan time in milliseconds, 0 = scan forever */
/** None of these are required as they will be handled by the library with defaults. **
** Remove as you see fit for your needs */
@@ -20,7 +20,7 @@ class ClientCallbacks : public NimBLEClientCallbacks {
void onDisconnect(NimBLEClient* pClient, int reason) override {
printf("%s Disconnected, reason = %d - Starting scan\n", pClient->getPeerAddress().toString().c_str(), reason);
NimBLEDevice::getScan()->start(scanTime, false, true);
NimBLEDevice::getScan()->start(scanTimeMs, false, true);
}
/********************* Security handled here *********************/
@@ -48,10 +48,10 @@ class ClientCallbacks : public NimBLEClientCallbacks {
return;
}
}
} clientCB;
} clientCallbacks;
/** Define a class to handle the callbacks when scan events are received */
class scanCallbacks : public NimBLEScanCallbacks {
class ScanCallbacks : public NimBLEScanCallbacks {
void onResult(const NimBLEAdvertisedDevice* advertisedDevice) override {
printf("Advertised Device found: %s\n", advertisedDevice->toString().c_str());
if (advertisedDevice->isAdvertisingService(NimBLEUUID("DEAD"))) {
@@ -68,9 +68,9 @@ class scanCallbacks : public NimBLEScanCallbacks {
/** Callback to process the results of the completed scan or restart it */
void onScanEnd(const NimBLEScanResults& results, int reason) override {
printf("Scan Ended, reason: %d, device count: %d; Restarting scan\n", reason, results.getCount());
NimBLEDevice::getScan()->start(scanTime, false, true);
NimBLEDevice::getScan()->start(scanTimeMs, false, true);
}
} scanCB;
} scanCallbacks;
/** Notification / Indication receiving handler callback */
void notifyCB(NimBLERemoteCharacteristic* pRemoteCharacteristic, uint8_t* pData, size_t length, bool isNotify) {
@@ -112,7 +112,7 @@ bool connectToServer() {
/** No client to reuse? Create a new one. */
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");
return false;
}
@@ -121,7 +121,7 @@ bool connectToServer() {
printf("New client created\n");
pClient->setClientCallbacks(&clientCB, false);
pClient->setClientCallbacks(&clientCallbacks, false);
/**
* Set initial connection parameters:
* These settings are safe for 3 clients to connect reliably, can go faster if you have less
@@ -241,8 +241,7 @@ bool connectToServer() {
return true;
}
extern "C"
void app_main(void) {
extern "C" void app_main(void) {
printf("Starting NimBLE Client\n");
/** Initialize NimBLE and set the device name */
NimBLEDevice::init("NimBLE-Client");
@@ -262,15 +261,14 @@ void app_main(void) {
* These are the default values, only shown here for demonstration.
*/
// NimBLEDevice::setSecurityAuth(false, false, true);
NimBLEDevice::setSecurityAuth(/*BLE_SM_PAIR_AUTHREQ_BOND | BLE_SM_PAIR_AUTHREQ_MITM |*/ BLE_SM_PAIR_AUTHREQ_SC);
// NimBLEDevice::setSecurityAuth(BLE_SM_PAIR_AUTHREQ_BOND | BLE_SM_PAIR_AUTHREQ_MITM | BLE_SM_PAIR_AUTHREQ_SC);
/** Optional: set the transmit power */
NimBLEDevice::setPower(3); // 9dbm
NimBLEDevice::setPower(3); /** 3dbm */
NimBLEScan* pScan = NimBLEDevice::getScan();
/** Set the callbacks to call when scan events occur, no duplicates */
pScan->setScanCallbacks(&scanCB, false);
pScan->setScanCallbacks(&scanCallbacks, false);
/** Set scan interval (how often) and window (how long) in milliseconds */
pScan->setInterval(100);
@@ -283,7 +281,7 @@ void app_main(void) {
pScan->setActiveScan(true);
/** Start scanning for advertisers */
pScan->start(scanTime);
pScan->start(scanTimeMs);
printf("Scanning for peripherals\n");
/** Loop here until we find a device we want to connect to */
@@ -299,7 +297,7 @@ void app_main(void) {
printf("Failed to connect, starting scan\n");
}
NimBLEDevice::getScan()->start(scanTime, false, true);
NimBLEDevice::getScan()->start(scanTimeMs, false, true);
}
}
}

View File

@@ -26,7 +26,7 @@ class ServerCallbacks : public NimBLEServerCallbacks {
* Latency: number of intervals allowed to skip.
* 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 {
@@ -142,8 +142,8 @@ extern "C" void app_main(void) {
* These are the default values, only shown here for demonstration.
*/
// NimBLEDevice::setSecurityAuth(false, false, true);
// NimBLEDevice::setSecurityAuth(BLE_SM_PAIR_AUTHREQ_BOND | BLE_SM_PAIR_AUTHREQ_MITM | BLE_SM_PAIR_AUTHREQ_SC);
NimBLEDevice::setSecurityAuth(/*BLE_SM_PAIR_AUTHREQ_BOND | BLE_SM_PAIR_AUTHREQ_MITM |*/ BLE_SM_PAIR_AUTHREQ_SC);
pServer = NimBLEDevice::createServer();
pServer->setCallbacks(&serverCallbacks);

View File

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

23
library.json Normal file
View File

@@ -0,0 +1,23 @@
{
"name": "esp-nimble-cpp",
"version": "2.3.3",
"description": "C++ wrapper for the NimBLE BLE stack",
"keywords": [
"BLE",
"espidf",
"espressif",
"esp32",
"nimble"
],
"license": "Apache-2.0",
"repository": {
"type": "git",
"url": "https://github.com/h2zero/esp-nimble-cpp"
},
"authors": {
"name": "Ryan Powell",
"email": "ryan@nable-embedded.io",
"url": "https://github.com/h2zero/esp-nimble-cpp",
"maintainer": true
}
}

View File

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

20
pkg.yml
View File

@@ -1,20 +0,0 @@
pkg.name: esp-nimble-cpp
pkg.type: lib
pkg.description: NimBLE CPP wrapper
pkg.author: "Ryan Powell"
pkg.homepage: "http://mynewt.apache.org/"
pkg.keywords:
pkg.deps:
- "@apache-mynewt-nimble/nimble"
- "@apache-mynewt-nimble/nimble/host"
- "@apache-mynewt-nimble/nimble/host/services/gap"
- "@apache-mynewt-nimble/nimble/host/services/gatt"
- "@apache-mynewt-nimble/nimble/host/store/config"
- "@apache-mynewt-nimble/nimble/host/util"
pkg.source_dirs:
- src
pkg.include_dirs:
- src

View File

@@ -1,21 +1,22 @@
/*
* NimBLE2904.cpp
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Created: on March 13, 2020
* Author H2zero
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* Originally:
* http://www.apache.org/licenses/LICENSE-2.0
*
* BLE2904.cpp
*
* Created on: Dec 23, 2017
* Author: kolban
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
# include "NimBLE2904.h"
#include "NimBLE2904.h"
#if CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_PERIPHERAL)
NimBLE2904::NimBLE2904(NimBLECharacteristic* pChr)
: NimBLEDescriptor(NimBLEUUID((uint16_t)0x2904), BLE_GATT_CHR_F_READ, sizeof(NimBLE2904Data), pChr) {
@@ -68,4 +69,4 @@ void NimBLE2904::setUnit(uint16_t unit) {
setValue(m_data);
} // setUnit
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL */
#endif // CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_PERIPHERAL)

View File

@@ -1,30 +1,33 @@
/*
* NimBLE2904.h
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Created: on March 13, 2020
* Author H2zero
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* Originally:
* http://www.apache.org/licenses/LICENSE-2.0
*
* BLE2904.h
*
* Created on: Dec 23, 2017
* Author: kolban
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef NIMBLE_CPP_2904_H_
#define NIMBLE_CPP_2904_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)
# include "NimBLEDescriptor.h"
struct NimBLE2904Data {
uint8_t m_format{0};
int8_t m_exponent{0};
uint16_t m_unit{0x2700}; // Unitless; See https://www.bluetooth.com/specifications/assigned-numbers/units
uint8_t m_namespace{1}; // 1 = Bluetooth SIG Assigned Numbers
uint16_t m_unit{0x2700}; // Unitless; See https://www.bluetooth.com/specifications/assigned-numbers/units
uint8_t m_namespace{1}; // 1 = Bluetooth SIG Assigned Numbers
uint16_t m_description{0}; // unknown description
} __attribute__((packed));
@@ -65,7 +68,7 @@ class NimBLE2904 : public NimBLEDescriptor {
static const uint8_t FORMAT_OPAQUE = 27;
static const uint8_t FORMAT_MEDASN1 = 28;
void setDescription(uint16_t);
void setDescription(uint16_t description);
void setExponent(int8_t exponent);
void setFormat(uint8_t format);
void setNamespace(uint8_t namespace_value);
@@ -76,5 +79,5 @@ class NimBLE2904 : public NimBLEDescriptor {
NimBLE2904Data m_data{};
}; // 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_

View File

@@ -1,24 +1,55 @@
/*
* NimBLEAddress.cpp
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Created: on Jan 24 2020
* Author H2zero
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* Originally:
* http://www.apache.org/licenses/LICENSE-2.0
*
* BLEAddress.cpp
*
* Created on: Jul 2, 2017
* Author: kolban
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED)
# include "NimBLEAddress.h"
#include "NimBLEAddress.h"
#if CONFIG_BT_NIMBLE_ENABLED
# include "NimBLELog.h"
# 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";
/*************************************************
@@ -41,7 +72,9 @@ NimBLEAddress::NimBLEAddress(ble_addr_t address) : ble_addr_t{address} {}
* ```
* which is 17 characters in length.
* @param [in] addr The hex string representation of the address.
* @param [in] type The type of the address.
* @param [in] type The type of the address, should be one of:
* * BLE_ADDR_PUBLIC (0)
* * BLE_ADDR_RANDOM (1)
*/
NimBLEAddress::NimBLEAddress(const std::string& addr, uint8_t type) {
this->type = type;
@@ -55,7 +88,7 @@ NimBLEAddress::NimBLEAddress(const std::string& addr, uint8_t type) {
std::string mac{addr};
mac.erase(std::remove(mac.begin(), mac.end(), ':'), mac.end());
uint64_t address = std::stoull(mac, nullptr, 16);
memcpy(this->val, &address, sizeof this->val);
memcpy(this->val, &address, sizeof(this->val));
return;
}
@@ -66,7 +99,9 @@ NimBLEAddress::NimBLEAddress(const std::string& addr, uint8_t type) {
/**
* @brief Constructor for compatibility with bluedroid esp library using native ESP representation.
* @param [in] address A uint8_t[6] or esp_bd_addr_t containing the address.
* @param [in] type The type of the address.
* @param [in] type The type of the address should be one of:
* * BLE_ADDR_PUBLIC (0)
* * BLE_ADDR_RANDOM (1)
*/
NimBLEAddress::NimBLEAddress(const uint8_t address[BLE_DEV_ADDR_LEN], uint8_t type) {
std::reverse_copy(address, address + BLE_DEV_ADDR_LEN, this->val);
@@ -77,10 +112,12 @@ NimBLEAddress::NimBLEAddress(const uint8_t address[BLE_DEV_ADDR_LEN], uint8_t ty
* @brief Constructor for address using a hex value.\n
* Use the same byte order, so use 0xa4c1385def16 for "a4:c1:38:5d:ef:16"
* @param [in] address uint64_t containing the address.
* @param [in] type The type of the address.
* @param [in] type The type of the address should be one of:
* * BLE_ADDR_PUBLIC (0)
* * BLE_ADDR_RANDOM (1)
*/
NimBLEAddress::NimBLEAddress(const uint64_t& address, uint8_t type) {
memcpy(this->val, &address, sizeof this->val);
memcpy(this->val, &address, sizeof(this->val));
this->type = type;
} // NimBLEAddress
@@ -183,7 +220,7 @@ bool NimBLEAddress::operator==(const NimBLEAddress& rhs) const {
return false;
}
return memcmp(rhs.val, this->val, sizeof this->val) == 0;
return memcmp(rhs.val, this->val, sizeof(this->val)) == 0;
} // operator ==
/**
@@ -201,12 +238,17 @@ NimBLEAddress::operator std::string() const {
char buffer[18];
snprintf(buffer,
sizeof(buffer),
"%02x:%02x:%02x:%02x:%02x:%02x",
NIMBLE_CPP_ADDR_FMT,
this->val[5],
NIMBLE_CPP_ADDR_DELIMITER,
this->val[4],
NIMBLE_CPP_ADDR_DELIMITER,
this->val[3],
NIMBLE_CPP_ADDR_DELIMITER,
this->val[2],
NIMBLE_CPP_ADDR_DELIMITER,
this->val[1],
NIMBLE_CPP_ADDR_DELIMITER,
this->val[0]);
return std::string{buffer};
} // operator std::string
@@ -216,7 +258,7 @@ NimBLEAddress::operator std::string() const {
*/
NimBLEAddress::operator uint64_t() const {
uint64_t address = 0;
memcpy(&address, this->val, sizeof this->val);
memcpy(&address, this->val, sizeof(this->val));
return address;
} // operator uint64_t

View File

@@ -1,21 +1,25 @@
/*
* NimBLEAddress.h
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Created: on Jan 24 2020
* Author H2zero
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* Originally:
* http://www.apache.org/licenses/LICENSE-2.0
*
* BLEAddress.h
*
* Created on: Jul 2, 2017
* Author: kolban
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef 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)
# include "nimble/ble.h"
@@ -31,9 +35,9 @@
# include <string>
/**
* @brief A %BLE device address.
* @brief A BLE device address.
*
* Every %BLE device has a unique address which can be used to identify it and form connections.
* Every BLE device has a unique address which can be used to identify it and form connections.
*/
class NimBLEAddress : private ble_addr_t {
public:
@@ -42,9 +46,9 @@ class NimBLEAddress : private ble_addr_t {
*/
NimBLEAddress() = default;
NimBLEAddress(const ble_addr_t address);
NimBLEAddress(const uint8_t address[BLE_DEV_ADDR_LEN], uint8_t type = BLE_ADDR_PUBLIC);
NimBLEAddress(const std::string& stringAddress, uint8_t type = BLE_ADDR_PUBLIC);
NimBLEAddress(const uint64_t& address, uint8_t type = BLE_ADDR_PUBLIC);
NimBLEAddress(const uint8_t address[BLE_DEV_ADDR_LEN], uint8_t type);
NimBLEAddress(const std::string& stringAddress, uint8_t type);
NimBLEAddress(const uint64_t& address, uint8_t type);
bool isRpa() const;
bool isNrpa() const;
@@ -59,9 +63,9 @@ class NimBLEAddress : private ble_addr_t {
const NimBLEAddress& reverseByteOrder();
bool operator==(const NimBLEAddress& rhs) const;
bool operator!=(const NimBLEAddress& rhs) const;
operator std::string() const;
operator uint64_t() const;
operator std::string() const;
operator uint64_t() const;
};
#endif /* CONFIG_BT_ENABLED */
#endif /* NIMBLE_CPP_ADDRESS_H_ */
#endif // CONFIG_BT_NIMBLE_ENABLED
#endif // NIMBLE_CPP_ADDRESS_H_

View File

@@ -1,22 +1,24 @@
/*
* NimBLEAdvertisedDevice.cpp
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Created: on Jan 24 2020
* Author H2zero
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* Originally:
* http://www.apache.org/licenses/LICENSE-2.0
*
* BLEAdvertisedDevice.cpp
*
* Created on: Jul 3, 2017
* Author: kolban
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
#include "NimBLEAdvertisedDevice.h"
#if CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_OBSERVER)
# include "NimBLEDevice.h"
# include "NimBLEAdvertisedDevice.h"
# include "NimBLEUtils.h"
# include "NimBLELog.h"
@@ -29,13 +31,14 @@ static const char* LOG_TAG = "NimBLEAdvertisedDevice";
* @param [in] event The advertisement event data.
*/
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_advType{eventType},
m_rssi{event->ext_disc.rssi},
m_callbackSent{0},
m_advLength{event->ext_disc.length_data},
m_isLegacyAdv{!!(event->ext_disc.props & BLE_HCI_ADV_LEGACY_MASK)},
m_dataStatus{event->ext_disc.data_status},
m_sid{event->ext_disc.sid},
m_primPhy{event->ext_disc.prim_phy},
m_secPhy{event->ext_disc.sec_phy},
@@ -56,9 +59,18 @@ NimBLEAdvertisedDevice::NimBLEAdvertisedDevice(const ble_gap_event* event, uint8
* @param [in] event The advertisement event data.
*/
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;
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
const auto& disc = event->disc;
# endif
@@ -84,11 +96,11 @@ const NimBLEAddress& NimBLEAdvertisedDevice::getAddress() const {
/**
* @brief Get the advertisement type.
* @return The advertising type the device is reporting:
* * BLE_HCI_ADV_TYPE_ADV_IND (0) - indirect advertising
* * BLE_HCI_ADV_TYPE_ADV_DIRECT_IND_HD (1) - direct advertising - high duty cycle
* * BLE_HCI_ADV_TYPE_ADV_SCAN_IND (2) - indirect scan response
* * BLE_HCI_ADV_TYPE_ADV_NONCONN_IND (3) - indirect advertising - not connectable
* * BLE_HCI_ADV_TYPE_ADV_DIRECT_IND_LD (4) - direct advertising - low duty cycle
* * BLE_HCI_ADV_RPT_EVTYPE_ADV_IND (0) - indirect advertising - connectable and scannable
* * BLE_HCI_ADV_RPT_EVTYPE_DIR_IND (1) - direct advertising - connectable
* * BLE_HCI_ADV_RPT_EVTYPE_SCAN_IND (2) - indirect scan response - not connectable - scannable
* * BLE_HCI_ADV_RPT_EVTYPE_NONCONN_IND (3) - beacon only - not connectable - not scannable
* * BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP (4) - scan response
*/
uint8_t NimBLEAdvertisedDevice::getAdvType() const {
return m_advType;
@@ -578,7 +590,7 @@ bool NimBLEAdvertisedDevice::haveTXPower() const {
return findAdvField(BLE_HS_ADV_TYPE_TX_PWR_LVL) > 0;
} // haveTXPower
# if CONFIG_BT_NIMBLE_EXT_ADV
# if MYNEWT_VAL(BLE_EXT_ADV)
/**
* @brief Get the set ID of the extended advertisement.
* @return The set ID.
@@ -615,6 +627,18 @@ uint8_t NimBLEAdvertisedDevice::getSecondaryPhy() const {
uint16_t NimBLEAdvertisedDevice::getPeriodicInterval() const {
return m_periodicItvl;
} // 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
uint8_t NimBLEAdvertisedDevice::findAdvField(uint8_t type, uint8_t index, size_t* data_loc) const {
@@ -728,7 +752,7 @@ std::string NimBLEAdvertisedDevice::toString() const {
* @brief Get the length of the advertisement data in the payload.
* @return The number of bytes in the payload that is from the advertisement.
*/
uint8_t NimBLEAdvertisedDevice::getAdvLength() const {
uint16_t NimBLEAdvertisedDevice::getAdvLength() const {
return m_advLength;
}
@@ -749,12 +773,13 @@ uint8_t NimBLEAdvertisedDevice::getAddressType() const {
* @return True if the device is connectable.
*/
bool NimBLEAdvertisedDevice::isConnectable() const {
# if CONFIG_BT_NIMBLE_EXT_ADV
if (m_isLegacyAdv) {
return m_advType == BLE_HCI_ADV_RPT_EVTYPE_ADV_IND || m_advType == BLE_HCI_ADV_RPT_EVTYPE_DIR_IND;
# if MYNEWT_VAL(BLE_EXT_ADV)
if (!m_isLegacyAdv) {
return (m_advType & BLE_HCI_ADV_CONN_MASK) || (m_advType & BLE_HCI_ADV_DIRECT_MASK);
}
# endif
return (m_advType & BLE_HCI_ADV_CONN_MASK) || (m_advType & BLE_HCI_ADV_DIRECT_MASK);
return m_advType == BLE_HCI_ADV_RPT_EVTYPE_ADV_IND || m_advType == BLE_HCI_ADV_RPT_EVTYPE_DIR_IND;
} // isConnectable
/**
@@ -770,13 +795,23 @@ bool NimBLEAdvertisedDevice::isScannable() const {
* @return True if legacy (Bluetooth 4.x), false if extended (bluetooth 5.x).
*/
bool NimBLEAdvertisedDevice::isLegacyAdvertisement() const {
# if CONFIG_BT_NIMBLE_EXT_ADV
# if MYNEWT_VAL(BLE_EXT_ADV)
return m_isLegacyAdv;
# else
return true;
# endif
} // 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.
* @return The advertisement payload.
@@ -801,4 +836,4 @@ const std::vector<uint8_t>::const_iterator NimBLEAdvertisedDevice::end() const {
return m_payload.cend();
}
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL */
#endif // CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_OBSERVER)

View File

@@ -1,22 +1,25 @@
/*
* NimBLEAdvertisedDevice.h
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Created: on Jan 24 2020
* Author H2zero
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* Originally:
* http://www.apache.org/licenses/LICENSE-2.0
*
* BLEAdvertisedDevice.h
*
* Created on: Jul 3, 2017
* Author: kolban
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef NIMBLE_CPP_ADVERTISED_DEVICE_H_
#define NIMBLE_CPP_ADVERTISED_DEVICE_H_
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
#include "syscfg/syscfg.h"
#if CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_OBSERVER)
# include "NimBLEAddress.h"
# include "NimBLEScan.h"
@@ -66,7 +69,7 @@ class NimBLEAdvertisedDevice {
NimBLEAddress getTargetAddress(uint8_t index = 0) const;
uint8_t getTargetAddressCount() const;
int8_t getTXPower() const;
uint8_t getAdvLength() const;
uint16_t getAdvLength() const;
uint8_t getAddressType() const;
bool isAdvertisingService(const NimBLEUUID& uuid) const;
bool haveAppearance() const;
@@ -84,12 +87,14 @@ class NimBLEAdvertisedDevice {
bool isConnectable() const;
bool isScannable() const;
bool isLegacyAdvertisement() const;
# if CONFIG_BT_NIMBLE_EXT_ADV
# if MYNEWT_VAL(BLE_EXT_ADV)
uint8_t getSetId() const;
uint8_t getPrimaryPhy() const;
uint8_t getSecondaryPhy() const;
uint16_t getPeriodicInterval() const;
uint8_t getDataStatus() const;
# endif
operator NimBLEAddress() const;
const std::vector<uint8_t>& getPayload() const;
const std::vector<uint8_t>::const_iterator begin() const;
@@ -157,10 +162,11 @@ class NimBLEAdvertisedDevice {
uint8_t m_advType{};
int8_t m_rssi{};
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{};
uint8_t m_dataStatus{};
uint8_t m_sid{};
uint8_t m_primPhy{};
uint8_t m_secPhy{};
@@ -170,5 +176,5 @@ class NimBLEAdvertisedDevice {
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_ */

View File

@@ -1,16 +1,23 @@
/*
* NimBLEAdvertisementData.cpp
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Created: on November 24, 2024
* Author H2zero
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "nimconfig.h"
#if (defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER) && !CONFIG_BT_NIMBLE_EXT_ADV) || \
defined(_DOXYGEN_)
#include "NimBLEAdvertisementData.h"
#if (CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_BROADCASTER) && !MYNEWT_VAL(BLE_EXT_ADV)) || defined(_DOXYGEN_)
# include "NimBLEAdvertisementData.h"
# include "NimBLEDevice.h"
# include "NimBLEUtils.h"
# include "NimBLEUUID.h"
@@ -30,7 +37,7 @@ static const char* LOG_TAG = "NimBLEAdvertisementData";
* @param [in] length The size of data to be added to the payload.
*/
bool NimBLEAdvertisementData::addData(const uint8_t* data, size_t length) {
if ((m_payload.size() + length) > BLE_HS_ADV_MAX_SZ) {
if (m_payload.size() + length > BLE_HS_ADV_MAX_SZ) {
NIMBLE_LOGE(LOG_TAG, "Data length exceeded");
return false;
}
@@ -101,7 +108,7 @@ bool NimBLEAdvertisementData::addTxPower() {
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();
data[2] = NimBLEDevice::getPower(NimBLETxPowerType::Advertise);
# else
data[2] = 0;
# endif
@@ -150,6 +157,7 @@ bool NimBLEAdvertisementData::addServiceUUID(const NimBLEUUID& serviceUUID) {
type = BLE_HS_ADV_TYPE_COMP_UUIDS128;
break;
default:
NIMBLE_LOGE(LOG_TAG, "Cannot add UUID, invalid size!");
return false;
}
@@ -160,10 +168,11 @@ bool NimBLEAdvertisementData::addServiceUUID(const NimBLEUUID& serviceUUID) {
}
if (length + getPayload().size() > BLE_HS_ADV_MAX_SZ) {
NIMBLE_LOGE(LOG_TAG, "Cannot add UUID, data length exceeded!");
return false;
}
uint8_t data[31];
uint8_t data[BLE_HS_ADV_MAX_SZ];
const uint8_t* uuid = serviceUUID.getValue();
if (dataLoc == -1) {
data[0] = 1 + bytes;
@@ -205,6 +214,7 @@ bool NimBLEAdvertisementData::removeServiceUUID(const NimBLEUUID& serviceUUID) {
type = BLE_HS_ADV_TYPE_COMP_UUIDS128;
break;
default:
NIMBLE_LOGE(LOG_TAG, "Cannot remove UUID, invalid size!");
return false;
}
@@ -257,12 +267,12 @@ bool NimBLEAdvertisementData::removeServices() {
* @return True if successful.
*/
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");
return false;
}
uint8_t mdata[31];
uint8_t mdata[BLE_HS_ADV_MAX_SZ];
mdata[0] = length + 1;
mdata[1] = BLE_HS_ADV_TYPE_MFG_DATA;
memcpy(&mdata[2], data, length);
@@ -293,12 +303,12 @@ bool NimBLEAdvertisementData::setManufacturerData(const std::vector<uint8_t>& da
* @return True if successful.
*/
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");
return false;
}
uint8_t data[31];
uint8_t data[BLE_HS_ADV_MAX_SZ];
uint8_t length = 2 + uri.length();
data[0] = length - 1;
data[1] = BLE_HS_ADV_TYPE_URI;
@@ -315,16 +325,16 @@ bool NimBLEAdvertisementData::setURI(const std::string& uri) {
* @return True if successful.
*/
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");
isComplete = false;
}
uint8_t data[31];
uint8_t length = 2 + std::min<uint8_t>(name.length(), 29);
uint8_t data[BLE_HS_ADV_MAX_SZ];
uint8_t length = 2 + std::min<uint8_t>(name.length(), BLE_HS_ADV_MAX_FIELD_SZ);
data[0] = length - 1;
data[1] = isComplete ? BLE_HS_ADV_TYPE_COMP_NAME : BLE_HS_ADV_TYPE_INCOMP_NAME;
memcpy(&data[2], name.c_str(), std::min<uint8_t>(name.length(), 29));
memcpy(&data[2], name.c_str(), std::min<uint8_t>(name.length(), BLE_HS_ADV_MAX_FIELD_SZ));
return addData(data, length);
} // setName
@@ -402,14 +412,14 @@ bool NimBLEAdvertisementData::setPartialServices32(const std::vector<NimBLEUUID>
bool NimBLEAdvertisementData::setServices(bool complete, uint8_t size, const std::vector<NimBLEUUID>& uuids) {
uint8_t bytes = size / 8;
uint8_t length = 2; // start with 2 for length + type bytes
uint8_t data[31];
uint8_t data[BLE_HS_ADV_MAX_SZ];
for (const auto& uuid : uuids) {
if (uuid.bitSize() != size) {
NIMBLE_LOGE(LOG_TAG, "Service UUID(%d) invalid", size);
continue;
} else {
if (length + bytes >= 31) {
if (length + bytes >= BLE_HS_ADV_MAX_SZ) {
NIMBLE_LOGW(LOG_TAG, "Too many services - truncating");
complete = false;
break;
@@ -432,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);
break;
default:
NIMBLE_LOGE(LOG_TAG, "Cannot set services, invalid size!");
return false;
}
@@ -449,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) {
uint8_t uuidBytes = uuid.bitSize() / 8;
uint8_t sDataLen = 2 + uuidBytes + length;
if (sDataLen > 31) {
if (sDataLen > BLE_HS_ADV_MAX_SZ) {
NIMBLE_LOGE(LOG_TAG, "Service Data too long");
return false;
}
@@ -466,6 +477,7 @@ bool NimBLEAdvertisementData::setServiceData(const NimBLEUUID& uuid, const uint8
type = BLE_HS_ADV_TYPE_SVC_DATA_UUID128;
break;
default:
NIMBLE_LOGE(LOG_TAG, "Cannot set service data, invalid size!");
return false;
}
@@ -474,7 +486,7 @@ bool NimBLEAdvertisementData::setServiceData(const NimBLEUUID& uuid, const uint8
return true;
}
uint8_t sData[31];
uint8_t sData[BLE_HS_ADV_MAX_SZ];
sData[0] = uuidBytes + length + 1;
sData[1] = type;
memcpy(&sData[2], uuid.getValue(), uuidBytes);
@@ -571,4 +583,4 @@ std::string NimBLEAdvertisementData::toString() const {
return str;
} // 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_)

View File

@@ -1,21 +1,29 @@
/*
* NimBLEAdvertisementData.h
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Created: on November 24, 2024
* Author H2zero
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef NIMBLE_CPP_ADVERTISEMENT_DATA_H_
#define NIMBLE_CPP_ADVERTISEMENT_DATA_H_
#include "nimconfig.h"
#if (defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER) && !CONFIG_BT_NIMBLE_EXT_ADV) || \
defined(_DOXYGEN_)
#include "syscfg/syscfg.h"
#if (CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_BROADCASTER) && !MYNEWT_VAL(BLE_EXT_ADV)) || defined(_DOXYGEN_)
#include <cstdint>
#include <string>
#include <vector>
# include <cstdint>
# include <string>
# include <vector>
class NimBLEUUID;
/**
@@ -29,7 +37,7 @@ class NimBLEAdvertisementData {
bool addData(const uint8_t* data, size_t length);
bool addData(const std::vector<uint8_t>& data);
bool setAppearance(uint16_t appearance);
bool setFlags(uint8_t);
bool setFlags(uint8_t flag);
bool addTxPower();
bool setPreferredParams(uint16_t minInterval, uint16_t maxInterval);
bool addServiceUUID(const NimBLEUUID& serviceUUID);
@@ -66,5 +74,5 @@ class NimBLEAdvertisementData {
std::vector<uint8_t> m_payload{};
}; // 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_

View File

@@ -1,29 +1,28 @@
/*
* NimBLEAdvertising.cpp
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Created: on March 3, 2020
* Author H2zero
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* Originally:
*
* BLEAdvertising.cpp
*
* This class encapsulates advertising a BLE Server.
* Created on: Jun 21, 2017
* Author: kolban
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "nimconfig.h"
#if (defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER) && !CONFIG_BT_NIMBLE_EXT_ADV) || \
defined(_DOXYGEN_)
#include "NimBLEAdvertising.h"
#if (CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_BROADCASTER) && !MYNEWT_VAL(BLE_EXT_ADV)) || defined(_DOXYGEN_)
# if defined(CONFIG_NIMBLE_CPP_IDF)
# include "services/gap/ble_svc_gap.h"
# else
# include "nimble/nimble/host/services/gap/include/services/gap/ble_svc_gap.h"
# endif
# include "NimBLEAdvertising.h"
# include "NimBLEDevice.h"
# include "NimBLEServer.h"
# include "NimBLEUtils.h"
@@ -43,7 +42,7 @@ NimBLEAdvertising::NimBLEAdvertising()
m_duration{BLE_HS_FOREVER},
m_scanResp{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;
# else
m_advParams.conn_mode = BLE_GAP_CONN_MODE_UND;
@@ -196,7 +195,7 @@ bool NimBLEAdvertising::start(uint32_t duration, const NimBLEAddress* dirAddr) {
return true;
}
# if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
# if MYNEWT_VAL(BLE_ROLE_PERIPHERAL)
NimBLEServer* pServer = NimBLEDevice::getServer();
if (pServer != nullptr) {
pServer->start(); // make sure the GATT server is ready before advertising
@@ -221,7 +220,7 @@ bool NimBLEAdvertising::start(uint32_t duration, const NimBLEAddress* dirAddr) {
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,
(dirAddr != nullptr) ? dirAddr->getBase() : NULL,
duration,
@@ -407,7 +406,7 @@ bool NimBLEAdvertising::refreshAdvertisingData() {
* @return True if the service was added successfully.
*/
bool NimBLEAdvertising::addServiceUUID(const NimBLEUUID& serviceUUID) {
if (!m_advData.addServiceUUID(serviceUUID) && m_scanResp) {
if (!m_advData.addServiceUUID(serviceUUID)) {
if (!m_scanData.addServiceUUID(serviceUUID)) {
return false;
}
@@ -465,7 +464,7 @@ bool NimBLEAdvertising::removeServices() {
* @return True if the appearance was set successfully.
*/
bool NimBLEAdvertising::setAppearance(uint16_t appearance) {
if (!m_advData.setAppearance(appearance) && m_scanResp) {
if (!m_advData.setAppearance(appearance)) {
if (!m_scanData.setAppearance(appearance)) {
return false;
}
@@ -483,7 +482,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.
*/
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)) {
return false;
}
@@ -498,7 +497,7 @@ bool NimBLEAdvertising::setPreferredParams(uint16_t minInterval, uint16_t maxInt
* @return True if the transmission power level was added successfully.
*/
bool NimBLEAdvertising::addTxPower() {
if (!m_advData.addTxPower() && m_scanResp) {
if (!m_advData.addTxPower()) {
if (!m_scanData.addTxPower()) {
return false;
}
@@ -536,7 +535,7 @@ bool NimBLEAdvertising::setName(const std::string& name) {
* @return True if the manufacturer data was set successfully.
*/
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)) {
return false;
}
@@ -570,7 +569,7 @@ bool NimBLEAdvertising::setManufacturerData(const std::vector<uint8_t>& data) {
* @return True if the URI was set successfully.
*/
bool NimBLEAdvertising::setURI(const std::string& uri) {
if (!m_advData.setURI(uri) && m_scanResp) {
if (!m_advData.setURI(uri)) {
if (!m_scanData.setURI(uri)) {
return false;
}
@@ -589,7 +588,7 @@ bool NimBLEAdvertising::setURI(const std::string& uri) {
* @note If data length is 0 the service data will not be advertised.
*/
bool NimBLEAdvertising::setServiceData(const NimBLEUUID& uuid, const uint8_t* data, size_t length) {
if (!m_advData.setServiceData(uuid, data, length) && m_scanResp) {
if (!m_advData.setServiceData(uuid, data, length)) {
if (!m_scanData.setServiceData(uuid, data, length)) {
return false;
}
@@ -621,4 +620,4 @@ bool NimBLEAdvertising::setServiceData(const NimBLEUUID& uuid, const std::string
return setServiceData(uuid, reinterpret_cast<const uint8_t*>(data.data()), data.length());
} // 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_)

View File

@@ -1,23 +1,25 @@
/*
* NimBLEAdvertising.h
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Created: on March 3, 2020
* Author H2zero
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* Originally:
* http://www.apache.org/licenses/LICENSE-2.0
*
* BLEAdvertising.h
*
* Created on: Jun 21, 2017
* Author: kolban
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef NIMBLE_CPP_ADVERTISING_H_
#define NIMBLE_CPP_ADVERTISING_H_
#include "nimconfig.h"
#if (defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER) && !CONFIG_BT_NIMBLE_EXT_ADV) || \
defined(_DOXYGEN_)
#include "syscfg/syscfg.h"
#if (CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_BROADCASTER) && !MYNEWT_VAL(BLE_EXT_ADV)) || defined(_DOXYGEN_)
# if defined(CONFIG_NIMBLE_CPP_IDF)
# include "host/ble_gap.h"
@@ -103,5 +105,5 @@ class NimBLEAdvertising {
bool m_advDataSet : 1;
};
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_BROADCASTER && !CONFIG_BT_NIMBLE_EXT_ADV */
#endif /* NIMBLE_CPP_ADVERTISING_H_ */
#endif // (CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_BROADCASTER) && !MYNEWT_VAL(BLE_EXT_ADV)) || defined(_DOXYGEN_)
#endif // NIMBLE_CPP_ADVERTISING_H_

View File

@@ -1,13 +1,22 @@
/*
* NimBLEAttValue.cpp
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Created: on July 17, 2024
* Author H2zero
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include "NimBLEAttValue.h"
#if CONFIG_BT_NIMBLE_ENABLED
# if defined(CONFIG_NIMBLE_CPP_IDF)
# include "nimble/nimble_npl.h"
@@ -15,7 +24,10 @@
# include "nimble/nimble/include/nimble/nimble_npl.h"
# endif
# include "NimBLEAttValue.h"
# include "NimBLEUtils.h"
# include "NimBLELog.h"
static const char* LOG_TAG = "NimBLEAttValue";
// Default constructor implementation.
NimBLEAttValue::NimBLEAttValue(uint16_t init_len, uint16_t max_len)
@@ -23,20 +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_len{},
m_capacity{init_len}
# if CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED
# if MYNEWT_VAL(NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED)
,
m_timestamp{}
# endif
{
NIMBLE_CPP_DEBUG_ASSERT(m_attr_value);
if (m_attr_value == nullptr) {
NIMBLE_LOGE(LOG_TAG, "Failed to calloc ctx");
}
}
// Value constructor implementation.
NimBLEAttValue::NimBLEAttValue(const uint8_t *value, uint16_t len, uint16_t max_len)
: NimBLEAttValue(len, max_len) {
memcpy(m_attr_value, value, len);
m_attr_value[len] = '\0';
m_attr_len = len;
NimBLEAttValue::NimBLEAttValue(const uint8_t* value, uint16_t len, uint16_t max_len) : NimBLEAttValue(len, max_len) {
if (m_attr_value != nullptr) {
memcpy(m_attr_value, value, len);
m_attr_len = len;
}
}
// Destructor implementation.
@@ -73,6 +88,10 @@ NimBLEAttValue& NimBLEAttValue::operator=(const NimBLEAttValue& source) {
void NimBLEAttValue::deepCopy(const NimBLEAttValue& source) {
uint8_t* res = static_cast<uint8_t*>(realloc(m_attr_value, source.m_capacity + 1));
NIMBLE_CPP_DEBUG_ASSERT(res);
if (res == nullptr) {
NIMBLE_LOGE(LOG_TAG, "Failed to realloc deepCopy");
return;
}
ble_npl_hw_enter_critical();
m_attr_value = res;
@@ -86,7 +105,8 @@ void NimBLEAttValue::deepCopy(const NimBLEAttValue& source) {
// Set the value of the attribute.
bool NimBLEAttValue::setValue(const uint8_t* value, uint16_t len) {
m_attr_len = 0; // Just set the value length to 0 and append instead of repeating code.
m_attr_len = 0; // Just set the value length to 0 and append instead of repeating code.
m_attr_value[0] = '\0'; // Set the first byte to 0 incase the len of the new value is 0.
append(value, len);
return memcmp(m_attr_value, value, len) == 0 && m_attr_len == len;
}
@@ -98,7 +118,7 @@ NimBLEAttValue& NimBLEAttValue::append(const uint8_t* value, uint16_t 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;
}
@@ -109,8 +129,12 @@ NimBLEAttValue& NimBLEAttValue::append(const uint8_t* value, uint16_t len) {
m_capacity = new_len;
}
NIMBLE_CPP_DEBUG_ASSERT(res);
if (res == nullptr) {
NIMBLE_LOGE(LOG_TAG, "Failed to realloc append");
return *this;
}
# if CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED
# if MYNEWT_VAL(NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED)
time_t t = time(nullptr);
# else
time_t t = 0;
@@ -127,4 +151,13 @@ NimBLEAttValue& NimBLEAttValue::append(const uint8_t* value, uint16_t len) {
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

View File

@@ -1,41 +1,60 @@
/*
* NimBLEAttValue.h
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Created: on March 18, 2021
* Author H2zero
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef 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
# include <Arduino.h>
# endif
# include "NimBLELog.h"
# include <string>
# include <vector>
# include <ctime>
# include <cstring>
# include <cstdint>
# ifndef CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED
# define CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED 0
# ifndef MYNEWT_VAL_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED
# 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
# ifndef BLE_ATT_ATTR_MAX_LEN
# define BLE_ATT_ATTR_MAX_LEN 512
# endif
# if !defined(CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH)
# define CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH 20
# elif CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH > BLE_ATT_ATTR_MAX_LEN
# error CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH cannot be larger than 512 (BLE_ATT_ATTR_MAX_LEN)
# elif CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH < 1
# error CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH cannot be less than 1; Range = 1 : 512
# ifndef MYNEWT_VAL_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH
# ifndef CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH
# define MYNEWT_VAL_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH 20
# else
# define MYNEWT_VAL_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH
# 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
/* Used to determine if the type passed to a template has a data() and size() method. */
@@ -54,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()))>
: 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.
* @details This class is designed to be more memory efficient than using\n
@@ -65,7 +92,7 @@ class NimBLEAttValue {
uint16_t m_attr_max_len{};
uint16_t m_attr_len{};
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{};
# endif
void deepCopy(const NimBLEAttValue& source);
@@ -76,7 +103,7 @@ class NimBLEAttValue {
* @param[in] init_len The initial size in bytes.
* @param[in] max_len The max size in bytes that the value can be.
*/
NimBLEAttValue(uint16_t init_len = CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH, uint16_t max_len = BLE_ATT_ATTR_MAX_LEN);
NimBLEAttValue(uint16_t init_len = 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.
@@ -161,7 +188,7 @@ class NimBLEAttValue {
/** @brief Iterator end */
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 */
time_t getTimeStamp() const { return m_timestamp; }
@@ -201,7 +228,7 @@ class NimBLEAttValue {
const NimBLEAttValue& getValue(time_t* timestamp = nullptr) const {
if (timestamp != nullptr) {
# if CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED
# if MYNEWT_VAL(NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED)
*timestamp = m_timestamp;
# else
*timestamp = 0;
@@ -255,13 +282,32 @@ class NimBLEAttValue {
/**
* @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.
* @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>
# ifdef _DOXYGEN_
bool
# 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
setValue(const T& v) {
return setValue(reinterpret_cast<const uint8_t*>(v.data()), v.size());
@@ -276,7 +322,11 @@ class NimBLEAttValue {
template <typename T>
typename std::enable_if<!std::is_pointer<T>::value, bool>::type setValue(const T& s) {
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) {
return setValue(reinterpret_cast<const uint8_t*>(s.c_str()), s.length());
} else {
@@ -297,27 +347,24 @@ class NimBLEAttValue {
*/
template <typename T>
T getValue(time_t* timestamp = nullptr, bool skipSizeCheck = false) const {
if (!skipSizeCheck && size() < sizeof(T)) {
return T();
}
if (timestamp != nullptr) {
# if CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED
# if MYNEWT_VAL(NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED)
*timestamp = m_timestamp;
# else
*timestamp = 0;
# endif
}
if (!skipSizeCheck && size() < sizeof(T)) {
return T();
}
return *(reinterpret_cast<const T*>(m_attr_value));
}
/*********************** Operators ************************/
/** @brief Subscript operator */
uint8_t operator[](int pos) const {
NIMBLE_CPP_DEBUG_ASSERT(pos < m_attr_len);
return m_attr_value[pos];
}
uint8_t operator[](int pos) const;
/** @brief Operator; Get the value as a std::vector<uint8_t>. */
operator std::vector<uint8_t>() const { return std::vector<uint8_t>(m_attr_value, m_attr_value + m_attr_len); }
@@ -357,5 +404,5 @@ class NimBLEAttValue {
# endif
};
#endif /*(CONFIG_BT_ENABLED) */
#endif /* NIMBLE_CPP_ATTVALUE_H_ */
#endif // CONFIG_BT_NIMBLE_ENABLED
#endif // NIMBLE_CPP_ATTVALUE_H_

View File

@@ -1,15 +1,25 @@
/*
* NimBLEAttribute.h
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Created: on July 28 2024
* Author H2zero
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef NIMBLE_CPP_ATTRIBUTE_H_
#define NIMBLE_CPP_ATTRIBUTE_H_
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && (defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) || defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL))
#include "syscfg/syscfg.h"
#if CONFIG_BT_NIMBLE_ENABLED && (MYNEWT_VAL(BLE_ROLE_PERIPHERAL) || MYNEWT_VAL(BLE_ROLE_CENTRAL))
# include "NimBLEUUID.h"
@@ -43,8 +53,8 @@ class NimBLEAttribute {
~NimBLEAttribute() = default;
const NimBLEUUID m_uuid{};
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_

View File

@@ -1,21 +1,23 @@
/*
* NimBLEBeacon.cpp
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Created: on March 15 2020
* Author H2zero
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* Originally:
* http://www.apache.org/licenses/LICENSE-2.0
*
* BLEBeacon.cpp
*
* Created on: Jan 4, 2018
* Author: kolban
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
#include "NimBLEBeacon.h"
#if CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_BROADCASTER)
# include "NimBLEBeacon.h"
# include "NimBLEUUID.h"
# include "NimBLELog.h"
@@ -135,4 +137,4 @@ void NimBLEBeacon::setSignalPower(int8_t signalPower) {
m_beaconData.signalPower = signalPower;
} // setSignalPower
#endif // CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_BROADCASTER
#endif // CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_BROADCASTER)

View File

@@ -1,26 +1,30 @@
/*
* NimBLEBeacon.h
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Created: on March 15 2020
* Author H2zero
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* Originally:
* http://www.apache.org/licenses/LICENSE-2.0
*
* BLEBeacon.h
*
* Created on: Jan 4, 2018
* Author: kolban
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef NIMBLE_CPP_BEACON_H_
#define NIMBLE_CPP_BEACON_H_
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
#include "syscfg/syscfg.h"
#if CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_BROADCASTER)
class NimBLEUUID;
# include <cstdint>
# include <vector>
/**
* @brief Representation of a beacon.
@@ -37,6 +41,10 @@ class NimBLEBeacon {
uint16_t major{};
uint16_t minor{};
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));
const BeaconData& getData();
@@ -57,5 +65,5 @@ class NimBLEBeacon {
BeaconData m_beaconData;
}; // NimBLEBeacon
#endif // CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_PERIPHERAL)
#endif // NIMBLE_CPP_BEACON_H_
#endif // CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL

View File

@@ -1,19 +1,30 @@
/*
* NimBLECharacteristic.cpp
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Created: on March 3, 2020
* Author H2zero
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* BLECharacteristic.cpp
* http://www.apache.org/licenses/LICENSE-2.0
*
* Created on: Jun 22, 2017
* Author: kolban
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#include "NimBLECharacteristic.h"
#if CONFIG_BT_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 "NimBLEDevice.h"
# include "NimBLELog.h"
@@ -214,7 +225,8 @@ void NimBLECharacteristic::setService(NimBLEService* pService) {
* @return True if the indication was sent successfully, false otherwise.
*/
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
/**
@@ -236,7 +248,8 @@ bool NimBLECharacteristic::indicate(const uint8_t* value, size_t length, uint16_
* @return True if the notification was sent successfully, false otherwise.
*/
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
/**
@@ -260,51 +273,159 @@ bool NimBLECharacteristic::notify(const uint8_t* value, size_t length, uint16_t
* @return True if the value was sent successfully, false otherwise.
*/
bool NimBLECharacteristic::sendValue(const uint8_t* value, size_t length, bool isNotification, uint16_t connHandle) const {
int rc = 0;
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
// Notify all connected peers unless a specific handle is provided
for (const auto& ch : NimBLEDevice::getServer()->getPeerDevices()) {
if (connHandle != BLE_HS_CONN_HANDLE_NONE && ch != connHandle) {
continue; // only send to the specific handle, minor inefficiency but saves code.
}
bool chSpecified = connHandle != BLE_HS_CONN_HANDLE_NONE;
bool requireSecure = m_properties & (BLE_GATT_CHR_F_READ_ENC | BLE_GATT_CHR_F_READ_AUTHEN | BLE_GATT_CHR_F_READ_AUTHOR);
int rc = chSpecified ? BLE_HS_ENOENT : 0; // if handle specified, assume not found until sent
// Must re-create the data buffer on each iteration because it is freed by the calls bellow.
os_mbuf* om = ble_hs_mbuf_from_flat(value, length);
if (!om) {
NIMBLE_LOGE(LOG_TAG, "<< sendValue: failed to allocate mbuf");
return false;
}
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;
}
// Notify all connected peers unless a specific handle is provided
for (const auto& entry : subs) {
uint16_t ch = entry.getConnHandle();
if (ch == BLE_HS_CONN_HANDLE_NONE || (chSpecified && ch != connHandle)) {
continue;
}
} 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) {
rc = ble_gattc_notify_custom(connHandle, m_handle, NULL);
rc = ble_gatts_notify_custom(ch, m_handle, om);
} 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
/**
* @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) {
m_pCallbacks->onRead(this, connInfo);
} // 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) {
setValue(val, len);
m_pCallbacks->onWrite(this, connInfo);
@@ -378,6 +499,18 @@ void NimBLECharacteristicCallbacks::onStatus(NimBLECharacteristic* pCharacterist
NIMBLE_LOGD("NimBLECharacteristicCallbacks", "onStatus: default");
} // 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.
* @param [in] pCharacteristic The characteristic that is the source of the event.
@@ -394,4 +527,4 @@ void NimBLECharacteristicCallbacks::onSubscribe(NimBLECharacteristic* pCharacter
NIMBLE_LOGD("NimBLECharacteristicCallbacks", "onSubscribe: default");
}
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL */
#endif // CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_PERIPHERAL)

View File

@@ -1,20 +1,25 @@
/*
* NimBLECharacteristic.h
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Created: on March 3, 2020
* Author H2zero
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* Originally:
* BLECharacteristic.h
* http://www.apache.org/licenses/LICENSE-2.0
*
* Created on: Jun 22, 2017
* Author: kolban
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef 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 NimBLEService;
@@ -26,6 +31,7 @@ class NimBLE2904;
# include <string>
# include <vector>
# include <array>
/**
* @brief The model of a BLE Characteristic.
@@ -83,7 +89,9 @@ class NimBLECharacteristic : public NimBLELocalValueAttribute {
# ifdef _DOXYGEN_
bool
# 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
notify(const T& v, uint16_t connHandle = BLE_HS_CONN_HANDLE_NONE) const {
return notify(reinterpret_cast<const uint8_t*>(&v), sizeof(T), connHandle);
@@ -105,7 +113,27 @@ 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] connHandle Optional, a connection handle to send the notification to.
*/
@@ -113,7 +141,7 @@ class NimBLECharacteristic : public NimBLELocalValueAttribute {
# ifdef _DOXYGEN_
bool
# 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
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);
@@ -129,7 +157,9 @@ class NimBLECharacteristic : public NimBLELocalValueAttribute {
# ifdef _DOXYGEN_
bool
# 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
indicate(const T& v, uint16_t connHandle = BLE_HS_CONN_HANDLE_NONE) const {
return indicate(reinterpret_cast<const uint8_t*>(&v), sizeof(T), connHandle);
@@ -151,7 +181,27 @@ 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] connHandle Optional, a connection handle to send the notification to.
*/
@@ -159,7 +209,7 @@ class NimBLECharacteristic : public NimBLELocalValueAttribute {
# ifdef _DOXYGEN_
bool
# 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
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);
@@ -178,10 +228,14 @@ class NimBLECharacteristic : public NimBLELocalValueAttribute {
* @note This function is only available if the type T is not a pointer.
*/
template <typename T>
typename std::enable_if<!std::is_pointer<T>::value, bool>::type notify(const T& value,
uint16_t connHandle = BLE_HS_CONN_HANDLE_NONE) const {
typename std::enable_if<!std::is_pointer<T>::value && !std::is_array<T>::value, bool>::type notify(
const T& value, uint16_t connHandle = BLE_HS_CONN_HANDLE_NONE) const {
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) {
return notify(reinterpret_cast<const uint8_t*>(value.c_str()), value.length(), connHandle);
} else {
@@ -200,10 +254,14 @@ class NimBLECharacteristic : public NimBLELocalValueAttribute {
* @note This function is only available if the type T is not a pointer.
*/
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 {
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) {
return indicate(reinterpret_cast<const uint8_t*>(value.c_str()), value.length(), connHandle);
} else {
@@ -224,9 +282,33 @@ class NimBLECharacteristic : public NimBLELocalValueAttribute {
bool is_notification = true,
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};
NimBLEService* m_pService{nullptr};
std::vector<NimBLEDescriptor*> m_vDescriptors{};
mutable SubPeerArray m_subPeers{};
}; // NimBLECharacteristic
/**
@@ -241,9 +323,10 @@ class NimBLECharacteristicCallbacks {
virtual ~NimBLECharacteristicCallbacks() {}
virtual void onRead(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);
};
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL */
#endif /*NIMBLE_CPP_CHARACTERISTIC_H_*/
#endif // CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_PERIPHERAL)
#endif // NIMBLE_CPP_CHARACTERISTIC_H_

View File

@@ -1,27 +1,30 @@
/*
* NimBLEClient.cpp
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Created: on Jan 26 2020
* Author H2zero
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* Originally:
* BLEClient.cpp
* http://www.apache.org/licenses/LICENSE-2.0
*
* Created on: Mar 22, 2017
* Author: kolban
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
#include "NimBLEClient.h"
#if CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_CENTRAL)
# include "NimBLEClient.h"
# include "NimBLERemoteService.h"
# include "NimBLERemoteCharacteristic.h"
# include "NimBLEDevice.h"
# include "NimBLELog.h"
# if defined(CONFIG_NIMBLE_CPP_IDF)
//# include "nimble/nimble_port.h"
# include "nimble/nimble_port.h"
# else
# include "nimble/porting/nimble/include/nimble/nimble_port.h"
# endif
@@ -65,7 +68,7 @@ NimBLEClient::NimBLEClient(const NimBLEAddress& peerAddress)
m_terminateFailCount{0},
m_asyncSecureAttempt{0},
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},
# endif
m_connParams{16,
@@ -122,6 +125,24 @@ size_t NimBLEClient::deleteService(const NimBLEUUID& uuid) {
return m_svcVec.size();
} // 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
* passed to the constructor.
@@ -137,22 +158,6 @@ bool NimBLEClient::connect(bool deleteAttributes, bool asyncConnect, bool exchan
return connect(m_peerAddress, deleteAttributes, asyncConnect, exchangeMTU);
} // 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.
* @param [in] address The address of the server.
@@ -199,7 +204,7 @@ bool NimBLEClient::connect(const NimBLEAddress& address, bool deleteAttributes,
m_config.exchangeMTU = exchangeMTU;
do {
# if CONFIG_BT_NIMBLE_EXT_ADV
# if MYNEWT_VAL(BLE_EXT_ADV)
rc = ble_gap_ext_connect(NimBLEDevice::m_ownAddrType,
peerAddr,
m_connectTimeout,
@@ -223,10 +228,15 @@ bool NimBLEClient::connect(const NimBLEAddress& address, bool deleteAttributes,
break;
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.
if (!NimBLEDevice::getScan()->stop()) {
rc = BLE_HS_EUNKNOWN;
}
# else
rc = BLE_HS_EUNKNOWN;
# endif
break;
case BLE_HS_EDONE:
@@ -304,7 +314,7 @@ bool NimBLEClient::secureConnection(bool async) const {
int rc = 0;
if (async && !NimBLEDevice::startSecurity(m_connHandle, &rc)) {
m_lastErr = rc;
m_lastErr = rc;
m_asyncSecureAttempt = 0;
return false;
}
@@ -392,7 +402,7 @@ void NimBLEClient::setConfig(NimBLEClient::Config config) {
m_config = config;
} // 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.
* @param [in] mask A bitmask indicating what PHYS to connect with.\n
@@ -404,6 +414,7 @@ void NimBLEClient::setConfig(NimBLEClient::Config config) {
void NimBLEClient::setConnectPhy(uint8_t mask) {
m_phyMask = mask;
} // setConnectPhy
# endif
/**
* @brief Request a change to the PHY used for this peer connection.
@@ -446,7 +457,6 @@ bool NimBLEClient::getPhy(uint8_t* txPhy, uint8_t* rxPhy) {
return rc == 0;
} // getPhy
# endif
/**
* @brief Set the connection parameters to use when connecting to a server.
@@ -923,9 +933,21 @@ int NimBLEClient::handleGapEvent(struct ble_gap_event* event, void* arg) {
switch (event->type) {
case BLE_GAP_EVENT_DISCONNECT: {
// workaround for bug in NimBLE stack where disconnect event argument is not passed correctly
pClient = NimBLEDevice::getClientByHandle(event->disconnect.conn.conn_handle);
pClient = NimBLEDevice::getClientByPeerAddress(event->disconnect.conn.peer_ota_addr);
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;
}
@@ -950,7 +972,9 @@ int NimBLEClient::handleGapEvent(struct ble_gap_event* event, void* arg) {
pClient->m_asyncSecureAttempt = 0;
// 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);
}
@@ -978,6 +1002,10 @@ int NimBLEClient::handleGapEvent(struct ble_gap_event* event, void* arg) {
}
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) {
pClient->m_connHandle = event->connect.conn_handle;
@@ -1137,7 +1165,6 @@ int NimBLEClient::handleGapEvent(struct ble_gap_event* event, void* arg) {
break;
} // BLE_GAP_EVENT_IDENTITY_RESOLVED
# if CONFIG_BT_NIMBLE_EXT_ADV
case BLE_GAP_EVENT_PHY_UPDATE_COMPLETE: {
NimBLEConnInfo peerInfo;
rc = ble_gap_conn_find(event->phy_updated.conn_handle, &peerInfo.m_desc);
@@ -1148,7 +1175,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);
return 0;
} // BLE_GAP_EVENT_PHY_UPDATE_COMPLETE
# endif
case BLE_GAP_EVENT_MTU: {
if (pClient->m_connHandle != event->mtu.conn_handle) {
@@ -1294,10 +1320,9 @@ void NimBLEClientCallbacks::onMTUChange(NimBLEClient* pClient, uint16_t mtu) {
NIMBLE_LOGD(CB_TAG, "onMTUChange: default");
} // onMTUChange
# if CONFIG_BT_NIMBLE_EXT_ADV
void NimBLEClientCallbacks::onPhyUpdate(NimBLEClient* pClient, uint8_t txPhy, uint8_t rxPhy) {
NIMBLE_LOGD(CB_TAG, "onPhyUpdate: default, txPhy: %d, rxPhy: %d", txPhy, rxPhy);
} // onPhyUpdate
# endif
#
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL */
#endif // CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_CENTRAL)

View File

@@ -1,21 +1,25 @@
/*
* NimBLEClient.h
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Created: on Jan 26 2020
* Author H2zero
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* Originally:
* BLEClient.h
* http://www.apache.org/licenses/LICENSE-2.0
*
* Created on: Mar 22, 2017
* Author: kolban
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef NIMBLE_CPP_CLIENT_H_
#define NIMBLE_CPP_CLIENT_H_
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
#include "syscfg/syscfg.h"
#if CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_CENTRAL)
# if defined(CONFIG_NIMBLE_CPP_IDF)
# include "host/ble_gap.h"
@@ -44,10 +48,12 @@ struct NimBLETaskData;
*/
class NimBLEClient {
public:
# if MYNEWT_VAL(BLE_ROLE_OBSERVER)
bool connect(const NimBLEAdvertisedDevice* device,
bool deleteAttributes = true,
bool asyncConnect = false,
bool exchangeMTU = true);
# endif
bool connect(const NimBLEAddress& address, bool deleteAttributes = true, bool asyncConnect = false, bool exchangeMTU = true);
bool connect(bool deleteAttributes = true, bool asyncConnect = false, bool exchangeMTU = true);
bool disconnect(uint8_t reason = BLE_ERR_REM_USER_CONN_TERM);
@@ -89,11 +95,11 @@ class NimBLEClient {
const NimBLEAttValue& value,
bool response = false);
# if CONFIG_BT_NIMBLE_EXT_ADV
# if MYNEWT_VAL(BLE_EXT_ADV)
void setConnectPhy(uint8_t phyMask);
# endif
bool updatePhy(uint8_t txPhysMask, uint8_t rxPhysMask, uint16_t phyOptions = 0);
bool getPhy(uint8_t* txPhy, uint8_t* rxPhy);
# endif
struct Config {
uint8_t deleteCallbacks : 1; // Delete the callback object when the client is deleted.
@@ -131,7 +137,7 @@ class NimBLEClient {
mutable uint8_t m_asyncSecureAttempt;
Config m_config;
# if CONFIG_BT_NIMBLE_EXT_ADV
# if MYNEWT_VAL(BLE_EXT_ADV)
uint8_t m_phyMask;
# endif
ble_gap_conn_params m_connParams;
@@ -209,7 +215,6 @@ class NimBLEClientCallbacks {
*/
virtual void onMTUChange(NimBLEClient* pClient, uint16_t MTU);
# if CONFIG_BT_NIMBLE_EXT_ADV
/**
* @brief Called when the PHY update procedure is complete.
* @param [in] pClient A pointer to the client whose PHY was updated.
@@ -222,8 +227,7 @@ class NimBLEClientCallbacks {
* * BLE_GAP_LE_PHY_CODED
*/
virtual void onPhyUpdate(NimBLEClient* pClient, uint8_t txPhy, uint8_t rxPhy);
# endif
};
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL */
#endif /* NIMBLE_CPP_CLIENT_H_ */
#endif // CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_CENTRAL)
#endif // NIMBLE_CPP_CLIENT_H_

View File

@@ -1,5 +1,22 @@
#ifndef NIMBLECONNINFO_H_
#define NIMBLECONNINFO_H_
/*
* 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_CONNINFO_H_
#define NIMBLE_CPP_CONNINFO_H_
#if defined(CONFIG_NIMBLE_CPP_IDF)
# include "host/ble_gap.h"
@@ -60,7 +77,8 @@ class NimBLEConnInfo {
friend class NimBLEDescriptor;
ble_gap_conn_desc m_desc{};
NimBLEConnInfo(){};
NimBLEConnInfo() {};
NimBLEConnInfo(ble_gap_conn_desc desc) { m_desc = desc; }
};
#endif
#endif // NIMBLE_CPP_CONNINFO_H_

View File

@@ -1,22 +1,24 @@
/*
* NimBLEDescriptor.cpp
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Created: on March 10, 2020
* Author H2zero
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* Originally:
* http://www.apache.org/licenses/LICENSE-2.0
*
* BLEDescriptor.cpp
*
* Created on: Jun 22, 2017
* Author: kolban
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#include "NimBLEDescriptor.h"
#if CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_PERIPHERAL)
# include "NimBLEService.h"
# include "NimBLEDescriptor.h"
# include "NimBLELog.h"
# include <string>
@@ -145,4 +147,4 @@ void NimBLEDescriptorCallbacks::onWrite(NimBLEDescriptor* pDescriptor, NimBLECon
NIMBLE_LOGD("NimBLEDescriptorCallbacks", "onWrite: default");
} // onWrite
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL */
#endif // CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_PERIPHERAL)

View File

@@ -1,34 +1,32 @@
/*
* NimBLEDescriptor.h
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Created: on March 10, 2020
* Author H2zero
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* Originally:
* http://www.apache.org/licenses/LICENSE-2.0
*
* BLEDescriptor.h
*
* Created on: Jun 22, 2017
* Author: kolban
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef NIMBLE_CPP_DESCRIPTOR_H_
#define NIMBLE_CPP_DESCRIPTOR_H_
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
class NimBLEDescriptor;
class NimBLEDescriptorCallbacks;
#include "syscfg/syscfg.h"
#if CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_PERIPHERAL)
# include "NimBLELocalValueAttribute.h"
# include "NimBLECharacteristic.h"
# include "NimBLEUUID.h"
# include "NimBLEAttValue.h"
# include "NimBLEConnInfo.h"
# include <string>
class NimBLECharacteristic;
class NimBLEDescriptorCallbacks;
/**
* @brief A model of a BLE descriptor.
*/
@@ -74,5 +72,5 @@ class NimBLEDescriptorCallbacks {
# include "NimBLE2904.h"
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL */
#endif /* NIMBLE_CPP_DESCRIPTOR_H_ */
#endif // CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_PERIPHERAL)
#endif // NIMBLE_CPP_DESCRIPTOR_H_

View File

@@ -1,22 +1,22 @@
/*
* NimBLEDevice.cpp
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Created: on Jan 24 2020
* Author H2zero
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* Originally:
* http://www.apache.org/licenses/LICENSE-2.0
*
* BLEDevice.cpp
*
* Created on: Mar 16, 2017
* Author: kolban
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED)
# include "NimBLEDevice.h"
# include "NimBLEUtils.h"
#include "NimBLEDevice.h"
#if CONFIG_BT_NIMBLE_ENABLED
# ifdef ESP_PLATFORM
# include "esp_err.h"
@@ -39,12 +39,7 @@
# include "nimble/esp_port/esp-hci/include/esp_nimble_hci.h"
# endif
# else
//# include "nimble/nimble/controller/include/controller/ble_phy.h"
# include "controller/ble_phy.h"
# include "host/ble_hs.h"
# include "host/util/util.h"
# include "services/gap/ble_svc_gap.h"
# include "services/gatt/ble_svc_gatt.h"
# include "nimble/nimble/controller/include/controller/ble_phy.h"
# endif
# ifndef CONFIG_NIMBLE_CPP_IDF
@@ -61,14 +56,6 @@
# include "esp32-hal-bt.h"
# 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"
static const char* LOG_TAG = "NimBLEDevice";
@@ -78,24 +65,30 @@ extern "C" void ble_store_config_init(void);
/**
* 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;
# endif
# if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
# if MYNEWT_VAL(BLE_ROLE_PERIPHERAL)
NimBLEServer* NimBLEDevice::m_pServer = nullptr;
# if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM)
NimBLEL2CAPServer* NimBLEDevice::m_pL2CAPServer = nullptr;
# endif
# endif
# if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
# if CONFIG_BT_NIMBLE_EXT_ADV
# if MYNEWT_VAL(BLE_ROLE_BROADCASTER)
# if MYNEWT_VAL(BLE_EXT_ADV)
NimBLEExtAdvertising* NimBLEDevice::m_bleAdvertising = nullptr;
# else
NimBLEAdvertising* NimBLEDevice::m_bleAdvertising = nullptr;
# endif
# endif
# if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
std::array<NimBLEClient*, NIMBLE_MAX_CONNECTIONS> NimBLEDevice::m_pClients{nullptr};
# if MYNEWT_VAL(BLE_ROLE_CENTRAL)
std::array<NimBLEClient*, MYNEWT_VAL(BLE_MAX_CONNECTIONS)> NimBLEDevice::m_pClients{};
# endif
bool NimBLEDevice::m_initialized{false};
@@ -106,9 +99,18 @@ std::vector<NimBLEAddress> NimBLEDevice::m_whiteList{};
uint8_t NimBLEDevice::m_ownAddrType{BLE_OWN_ADDR_PUBLIC};
# ifdef ESP_PLATFORM
# ifdef CONFIG_BTDM_BLE_SCAN_DUPL
# if CONFIG_BTDM_BLE_SCAN_DUPL
uint16_t NimBLEDevice::m_scanDuplicateSize{CONFIG_BTDM_SCAN_DUPL_CACHE_SIZE};
uint8_t NimBLEDevice::m_scanFilterMode{CONFIG_BTDM_SCAN_DUPL_TYPE};
uint16_t NimBLEDevice::m_scanDuplicateResetTime{0};
# elif CONFIG_BT_LE_SCAN_DUPL
uint16_t NimBLEDevice::m_scanDuplicateSize{CONFIG_BT_LE_LL_DUP_SCAN_LIST_COUNT};
uint8_t NimBLEDevice::m_scanFilterMode{CONFIG_BT_LE_SCAN_DUPL_TYPE};
uint16_t NimBLEDevice::m_scanDuplicateResetTime{0};
extern "C" int ble_vhci_disc_duplicate_set_max_cache_size(int max_cache_size);
extern "C" int ble_vhci_disc_duplicate_set_period_refresh_time(int refresh_period_time);
extern "C" int ble_vhci_disc_duplicate_mode_disable(int mode);
extern "C" int ble_vhci_disc_duplicate_mode_enable(int mode);
# endif
# endif
@@ -116,7 +118,7 @@ uint8_t NimBLEDevice::m_scanFilterMode{CONFIG_BTDM_SCAN_DUPL_TYPE};
/* SERVER FUNCTIONS */
/* -------------------------------------------------------------------------- */
# if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
# if MYNEWT_VAL(BLE_ROLE_PERIPHERAL)
/**
* @brief Create an instance of a server.
* @return A pointer to the instance of the server.
@@ -139,14 +141,35 @@ NimBLEServer* NimBLEDevice::createServer() {
NimBLEServer* NimBLEDevice::getServer() {
return m_pServer;
} // 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 */
/* -------------------------------------------------------------------------- */
# if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
# if CONFIG_BT_NIMBLE_EXT_ADV
# if MYNEWT_VAL(BLE_ROLE_BROADCASTER)
# if MYNEWT_VAL(BLE_EXT_ADV)
/**
* @brief Get the instance of the extended advertising object.
* @return A pointer to the extended advertising object.
@@ -180,7 +203,7 @@ bool NimBLEDevice::stopAdvertising(uint8_t instId) {
} // stopAdvertising
# 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.
* @return A pointer to the advertising object.
@@ -209,7 +232,7 @@ bool NimBLEDevice::startAdvertising(uint32_t duration) {
bool NimBLEDevice::stopAdvertising() {
return getAdvertising()->stop();
} // stopAdvertising
# endif // #if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
# endif // #if MYNEWT_VAL(BLE_ROLE_BROADCASTER)
/* -------------------------------------------------------------------------- */
/* SCAN FUNCTIONS */
@@ -220,7 +243,7 @@ bool NimBLEDevice::stopAdvertising() {
* @return The scanning object reference. This is a singleton object. The caller should not
* try and release/delete it.
*/
# if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
# if MYNEWT_VAL(BLE_ROLE_OBSERVER)
NimBLEScan* NimBLEDevice::getScan() {
if (m_pScan == nullptr) {
m_pScan = new NimBLEScan();
@@ -230,7 +253,7 @@ NimBLEScan* NimBLEDevice::getScan() {
} // getScan
# 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.
* @param [in] size The number of advertisements filtered before the cache is reset.\n
@@ -277,15 +300,34 @@ void NimBLEDevice::setScanFilterMode(uint8_t 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 // #if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
# endif // MYNEWT_VAL(BLE_ROLE_OBSERVER)
/* -------------------------------------------------------------------------- */
/* 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.
* @return A pointer to the new client object, or nullptr on error.
@@ -308,7 +350,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;
} // createClient
@@ -420,7 +462,7 @@ std::vector<NimBLEClient*> NimBLEDevice::getConnectedClients() {
return clients;
} // getConnectedClients
# endif // #if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
# endif // MYNEWT_VAL(BLE_ROLE_CENTRAL)
/* -------------------------------------------------------------------------- */
/* TRANSMIT POWER */
@@ -466,7 +508,7 @@ bool NimBLEDevice::setPowerLevel(esp_power_level_t powerLevel, esp_ble_power_typ
* @param [in] dbm The power level to set in dBm.
* @return True if the power level was set successfully.
*/
bool NimBLEDevice::setPower(int8_t dbm) {
bool NimBLEDevice::setPower(int8_t dbm, NimBLETxPowerType type) {
# ifdef ESP_PLATFORM
# ifdef CONFIG_IDF_TARGET_ESP32P4
return false; // CONFIG_IDF_TARGET_ESP32P4 does not support esp_ble_tx_power_set
@@ -474,19 +516,33 @@ bool NimBLEDevice::setPower(int8_t dbm) {
if (dbm % 3 == 2) {
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
# else
(void)type; // unused
NIMBLE_LOGD(LOG_TAG, ">> setPower: %d", dbm);
ble_hci_vs_set_tx_pwr_cp cmd{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));
int rc = ble_phy_tx_power_set(dbm);
if (rc) {
NIMBLE_LOGE(LOG_TAG, "failed to set TX power, rc: %04x\n", rc);
return false;
}
NIMBLE_LOGD(LOG_TAG, "TX power set to %d dBm\n", rsp.tx_power);
NIMBLE_LOGD(LOG_TAG, "TX power set to %d dBm\n", dbm);
return true;
# endif
} // setPower
@@ -495,12 +551,16 @@ bool NimBLEDevice::setPower(int8_t dbm) {
* @brief Get the transmission power.
* @return The power level currently used in dbm or 0xFF on error.
*/
int NimBLEDevice::getPower() {
int NimBLEDevice::getPower(NimBLETxPowerType type) {
# ifdef ESP_PLATFORM
# ifdef CONFIG_IDF_TARGET_ESP32P4
return 0xFF; // CONFIG_IDF_TARGET_ESP32P4 does not support esp_ble_tx_power_get
# else
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) {
NIMBLE_LOGE(LOG_TAG, "esp_ble_tx_power_get failed rc=%d", pwr);
return 0xFF;
@@ -517,7 +577,8 @@ int NimBLEDevice::getPower() {
return 0;
# endif
# else
return ble_phy_tx_power_get(); //ble_phy_txpwr_get();
(void)type; // unused
return ble_phy_tx_power_get();
# endif
} // getPower
@@ -552,7 +613,7 @@ uint16_t NimBLEDevice::getMTU() {
/* 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
*/
@@ -616,18 +677,14 @@ bool NimBLEDevice::isBonded(const NimBLEAddress& address) {
/**
* @brief Get the address of a bonded peer device by index.
* @param [in] index The index to retrieve the peer address of.
* @returns NimBLEAddress of the found bonded peer or nullptr if not found.
* @returns NimBLEAddress of the found bonded peer or null address if not found.
*/
NimBLEAddress NimBLEDevice::getBondedAddress(int index) {
ble_addr_t peer_id_addrs[MYNEWT_VAL(BLE_STORE_MAX_BONDS)];
int num_peers, rc;
rc = ble_store_util_bonded_peers(&peer_id_addrs[0], &num_peers, MYNEWT_VAL(BLE_STORE_MAX_BONDS));
if (rc != 0) {
return nullptr;
}
if (index > num_peers || index < 0) {
return nullptr;
if (rc != 0 || index > num_peers || index < 0) {
return NimBLEAddress{};
}
return NimBLEAddress(peer_id_addrs[index]);
@@ -706,12 +763,12 @@ size_t NimBLEDevice::getWhiteListCount() {
/**
* @brief Gets the address at the vector index.
* @param [in] index The vector index to retrieve the address from.
* @returns The NimBLEAddress at the whitelist index or nullptr if not found.
* @returns The NimBLEAddress at the whitelist index or null address if not found.
*/
NimBLEAddress NimBLEDevice::getWhiteListAddress(size_t index) {
if (index > m_whiteList.size()) {
NIMBLE_LOGE(LOG_TAG, "Invalid index; %u", index);
return nullptr;
return NimBLEAddress{};
}
return m_whiteList[index];
@@ -721,7 +778,6 @@ NimBLEAddress NimBLEDevice::getWhiteListAddress(size_t index) {
/* STACK FUNCTIONS */
/* -------------------------------------------------------------------------- */
# if CONFIG_BT_NIMBLE_EXT_ADV || defined(_DOXYGEN_)
/**
* @brief Set the preferred default phy to use for connections.
* @param [in] txPhyMask TX PHY. Can be mask of following:
@@ -744,7 +800,6 @@ bool NimBLEDevice::setDefaultPhy(uint8_t txPhyMask, uint8_t rxPhyMask) {
return rc == 0;
}
# endif
/**
* @brief Host reset, we pass the message so we don't make calls until re-synced.
@@ -795,13 +850,13 @@ void NimBLEDevice::onSync(void) {
m_synced = true;
if (m_initialized) {
# if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
# if MYNEWT_VAL(BLE_ROLE_OBSERVER)
if (m_pScan != nullptr) {
m_pScan->onHostSync();
}
# endif
# if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
# if MYNEWT_VAL(BLE_ROLE_BROADCASTER)
if (m_bleAdvertising != nullptr) {
m_bleAdvertising->onHostSync();
}
@@ -814,19 +869,10 @@ void NimBLEDevice::onSync(void) {
*/
void NimBLEDevice::host_task(void* param) {
NIMBLE_LOGI(LOG_TAG, "BLE Host Task Started");
#ifndef MYNEWT
nimble_port_run(); // This function will return only when nimble_port_stop() is executed
nimble_port_freertos_deinit();
#else
while (1) {
os_eventq_run(os_eventq_dflt_get());
}
#endif
} // host_task
static struct os_task ble_nimble_task;
static os_stack_t ble_nimble_stack[1024];
/**
* @brief Initialize the BLE environment.
* @param [in] deviceName The device name of the device.
@@ -835,7 +881,7 @@ bool NimBLEDevice::init(const std::string& deviceName) {
if (!m_initialized) {
# ifdef ESP_PLATFORM
# ifdef CONFIG_ENABLE_ARDUINO_DEPENDS
# if defined(CONFIG_ENABLE_ARDUINO_DEPENDS) && SOC_BT_SUPPORTED
// make sure the linker includes esp32-hal-bt.c so Arduino init doesn't release BLE memory.
btStarted();
# endif
@@ -857,18 +903,26 @@ bool NimBLEDevice::init(const std::string& deviceName) {
esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT);
# endif
# if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0) | !defined(CONFIG_NIMBLE_CPP_IDF)
# if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0) || !defined(CONFIG_NIMBLE_CPP_IDF)
esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
# if defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S3)
bt_cfg.bluetooth_mode = ESP_BT_MODE_BLE;
# else
# if defined(CONFIG_IDF_TARGET_ESP32)
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
# ifdef CONFIG_BTDM_BLE_SCAN_DUPL
# if CONFIG_BTDM_BLE_SCAN_DUPL
bt_cfg.normal_adv_size = m_scanDuplicateSize;
bt_cfg.scan_duplicate_type = m_scanFilterMode;
# if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
bt_cfg.dup_list_refresh_period = m_scanDuplicateResetTime;
# endif
# elif CONFIG_BT_LE_SCAN_DUPL
bt_cfg.ble_ll_rsp_dup_list_count = m_scanDuplicateSize;
bt_cfg.ble_ll_adv_dup_list_count = m_scanDuplicateSize;
# endif
err = esp_bt_controller_init(&bt_cfg);
if (err != ESP_OK) {
@@ -876,44 +930,64 @@ bool NimBLEDevice::init(const std::string& deviceName) {
return false;
}
# if CONFIG_BT_LE_SCAN_DUPL
int mode = (1UL << 4); // FILTER_DUPLICATE_EXCEPTION_FOR_MESH
switch (m_scanFilterMode) {
case 1:
mode |= (1UL << 3); // FILTER_DUPLICATE_ADVDATA
break;
case 2:
mode |= ((1UL << 2) | (1UL << 3)); // FILTER_DUPLICATE_ADDRESS | FILTER_DUPLICATE_ADVDATA
break;
default:
mode |= (1UL << 0) | (1UL << 2); // FILTER_DUPLICATE_PDUTYPE | FILTER_DUPLICATE_ADDRESS
}
ble_vhci_disc_duplicate_mode_disable(0xFFFFFFFF);
ble_vhci_disc_duplicate_mode_enable(mode);
ble_vhci_disc_duplicate_set_max_cache_size(m_scanDuplicateSize);
ble_vhci_disc_duplicate_set_period_refresh_time(m_scanDuplicateResetTime);
# endif
err = esp_bt_controller_enable(ESP_BT_MODE_BLE);
if (err != ESP_OK) {
NIMBLE_LOGE(LOG_TAG, "esp_bt_controller_enable() failed; err=%d", err);
return false;
}
# if CONFIG_BT_NIMBLE_LEGACY_VHCI_ENABLE
err = esp_nimble_hci_init();
if (err != ESP_OK) {
NIMBLE_LOGE(LOG_TAG, "esp_nimble_hci_init() failed; err=%d", err);
return false;
}
# endif
# endif
# endif
#ifndef MYNEWT
nimble_port_init();
#endif
// Setup callbacks for host events
ble_hs_cfg.reset_cb = NimBLEDevice::onReset;
ble_hs_cfg.sync_cb = NimBLEDevice::onSync;
ble_hs_cfg.reset_cb = NimBLEDevice::onReset;
ble_hs_cfg.sync_cb = NimBLEDevice::onSync;
ble_hs_cfg.store_status_cb = [](struct ble_store_status_event* event, void* arg) {
return m_pDeviceCallbacks->onStoreStatus(event, arg);
};
// Set initial security capabilities
ble_hs_cfg.sm_io_cap = BLE_HS_IO_NO_INPUT_OUTPUT;
ble_hs_cfg.sm_bonding = 0;
ble_hs_cfg.sm_mitm = 0;
ble_hs_cfg.sm_sc = 1;
ble_hs_cfg.sm_our_key_dist = BLE_SM_PAIR_KEY_DIST_ENC | BLE_SM_PAIR_KEY_DIST_ID;
ble_hs_cfg.sm_their_key_dist = BLE_SM_PAIR_KEY_DIST_ENC | BLE_SM_PAIR_KEY_DIST_ID;
ble_hs_cfg.store_status_cb = ble_store_util_status_rr; /*TODO: Implement handler for this*/
ble_hs_cfg.sm_sc = 0;
ble_hs_cfg.sm_our_key_dist = BLE_SM_PAIR_KEY_DIST_ENC;
ble_hs_cfg.sm_their_key_dist = BLE_SM_PAIR_KEY_DIST_ENC;
# if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY)
ble_hs_cfg.sm_our_key_dist |= BLE_SM_PAIR_KEY_DIST_ID;
ble_hs_cfg.sm_their_key_dist |= BLE_SM_PAIR_KEY_DIST_ID;
# endif
setDeviceName(deviceName);
#ifndef MYNEWT
ble_store_config_init();
nimble_port_freertos_init(NimBLEDevice::host_task);
#else
os_task_init(&ble_nimble_task, "ble_nimble_task", NimBLEDevice::host_task, NULL, 8,
OS_WAIT_FOREVER, ble_nimble_stack, 1024);
#endif
}
// Wait for host and controller to sync before returning and accepting new tasks
@@ -935,7 +1009,6 @@ bool NimBLEDevice::init(const std::string& deviceName) {
bool NimBLEDevice::deinit(bool clearAll) {
int rc = 0;
if (m_initialized) {
#ifndef MYNEWT
rc = nimble_port_stop();
if (rc == 0) {
nimble_port_deinit();
@@ -947,38 +1020,40 @@ bool NimBLEDevice::deinit(bool clearAll) {
}
# endif
# endif
#else
rc = ble_hs_shutdown(0);
if (rc == 0) {
#endif
m_initialized = false;
m_synced = false;
}
}
if (clearAll) {
# if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
# if MYNEWT_VAL(BLE_ROLE_PERIPHERAL)
if (NimBLEDevice::m_pServer != nullptr) {
delete NimBLEDevice::m_pServer;
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
# if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
# if MYNEWT_VAL(BLE_ROLE_BROADCASTER)
if (NimBLEDevice::m_bleAdvertising != nullptr) {
delete NimBLEDevice::m_bleAdvertising;
NimBLEDevice::m_bleAdvertising = nullptr;
}
# endif
# if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
# if MYNEWT_VAL(BLE_ROLE_OBSERVER)
if (NimBLEDevice::m_pScan != nullptr) {
delete NimBLEDevice::m_pScan;
NimBLEDevice::m_pScan = nullptr;
}
# endif
# if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
# if MYNEWT_VAL(BLE_ROLE_CENTRAL)
for (auto clt : m_pClients) {
deleteClient(clt);
}
@@ -1035,18 +1110,16 @@ bool NimBLEDevice::setOwnAddrType(uint8_t 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) {
# ifdef CONFIG_IDF_TARGET_ESP32
// esp32 controller does not support RPA so we must use the random static for calls to the stack
// the host will take care of the random private address generation/setting.
m_ownAddrType = BLE_OWN_ADDR_RANDOM;
rc = ble_hs_pvcy_rpa_config(NIMBLE_HOST_ENABLE_RPA);
# endif
} else {
# ifdef CONFIG_IDF_TARGET_ESP32
rc = ble_hs_pvcy_rpa_config(NIMBLE_HOST_DISABLE_PRIVACY);
# endif
}
# endif
return rc == 0;
} // setOwnAddrType
@@ -1181,6 +1254,7 @@ bool NimBLEDevice::startSecurity(uint16_t connHandle, int* rcPtr) {
return rc == 0 || rc == BLE_HS_EALREADY;
} // startSecurity
# if MYNEWT_VAL(BLE_ROLE_CENTRAL) || MYNEWT_VAL(BLE_ROLE_PERIPHERAL)
/**
* @brief Inject the provided passkey into the Security Manager.
* @param [in] peerInfo Connection information for the peer.
@@ -1205,6 +1279,7 @@ bool NimBLEDevice::injectConfirmPasskey(const NimBLEConnInfo& peerInfo, bool acc
NIMBLE_LOGD(LOG_TAG, "BLE_SM_IOACT_NUMCMP; ble_sm_inject_io result: %d", rc);
return rc == 0;
}
# endif // MYNEWT_VAL(BLE_ROLE_CENTRAL) || MYNEWT_VAL(BLE_ROLE_PERIPHERAL)
/* -------------------------------------------------------------------------- */
/* UTILITIES */
@@ -1215,12 +1290,13 @@ bool NimBLEDevice::injectConfirmPasskey(const NimBLEConnInfo& peerInfo, bool acc
* @param [in] deviceName The name to set.
*/
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());
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "Device name not set - too long");
return false;
}
#endif
return true;
} // setDeviceName
@@ -1249,16 +1325,26 @@ std::string NimBLEDevice::toString() {
return getAddress().toString();
} // toString
# if CONFIG_NIMBLE_CPP_DEBUG_ASSERT_ENABLED || __DOXYGEN__
# if MYNEWT_VAL(NIMBLE_CPP_DEBUG_ASSERT_ENABLED) || __DOXYGEN__
/**
* @brief Debug assert - weak function.
* @param [in] file The file where the assert occurred.
* @param [in] line The line number where the assert occurred.
*/
void nimble_cpp_assert(const char* file, unsigned line) {
NIMBLE_LOGC("", "Assertion failed at %s:%u\n", file, line);
console_printf("Assertion failed at %s:%u\n", file, line);
ble_npl_time_delay(10);
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

View File

@@ -1,22 +1,25 @@
/*
* NimBLEDevice.h
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Created: on Jan 24 2020
* Author H2zero
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* Originally:
* http://www.apache.org/licenses/LICENSE-2.0
*
* BLEDevice.h
*
* Created on: Mar 16, 2017
* Author: kolban
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef NIMBLE_CPP_DEVICE_H_
#define NIMBLE_CPP_DEVICE_H_
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include "syscfg/syscfg.h"
#if CONFIG_BT_NIMBLE_ENABLED
# ifdef ESP_PLATFORM
# ifndef CONFIG_IDF_TARGET_ESP32P4
# include <esp_bt.h>
@@ -37,32 +40,36 @@
# include <string>
# include <vector>
# if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
# if MYNEWT_VAL(BLE_ROLE_CENTRAL)
# include <array>
class NimBLEClient;
# endif
# if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
# if MYNEWT_VAL(BLE_ROLE_OBSERVER)
class NimBLEScan;
# endif
# if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
# if CONFIG_BT_NIMBLE_EXT_ADV
# if MYNEWT_VAL(BLE_ROLE_BROADCASTER)
# if MYNEWT_VAL(BLE_EXT_ADV)
class NimBLEExtAdvertising;
# else
class NimBLEAdvertising;
# endif
# endif
# if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
# if MYNEWT_VAL(BLE_ROLE_PERIPHERAL)
class NimBLEServer;
# if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM)
class NimBLEL2CAPServer;
# 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;
# endif
class NimBLEAddress;
class NimBLEDeviceCallbacks;
# define BLEDevice NimBLEDevice
# define BLEClient NimBLEClient
@@ -91,12 +98,15 @@ class NimBLEAddress;
# define BLEEddystoneTLM NimBLEEddystoneTLM
# define BLEEddystoneURL NimBLEEddystoneURL
# define BLEConnInfo NimBLEConnInfo
# define BLEL2CAPServer NimBLEL2CAPServer
# define BLEL2CAPService NimBLEL2CAPService
# define BLEL2CAPServiceCallbacks NimBLEL2CAPServiceCallbacks
# define BLEL2CAPClient NimBLEL2CAPClient
# define BLEL2CAPClientCallbacks NimBLEL2CAPClientCallbacks
# define BLEL2CAPChannel NimBLEL2CAPChannel
# define BLEL2CAPChannelCallbacks NimBLEL2CAPChannelCallbacks
# ifdef CONFIG_BT_NIMBLE_MAX_CONNECTIONS
# define NIMBLE_MAX_CONNECTIONS CONFIG_BT_NIMBLE_MAX_CONNECTIONS
# else
# define NIMBLE_MAX_CONNECTIONS CONFIG_NIMBLE_MAX_CONNECTIONS
# endif
enum class NimBLETxPowerType { All = 0, Advertise = 1, Scan = 2, Connection = 3 };
typedef int (*gap_event_handler)(ble_gap_event* event, void* arg);
@@ -119,8 +129,10 @@ class NimBLEDevice {
static bool setOwnAddrType(uint8_t type);
static bool setOwnAddr(const NimBLEAddress& addr);
static bool setOwnAddr(const uint8_t* addr);
static void setDeviceCallbacks(NimBLEDeviceCallbacks* cb);
static void setScanDuplicateCacheSize(uint16_t cacheSize);
static void setScanFilterMode(uint8_t type);
static void setScanDuplicateCacheResetTime(uint16_t time);
static bool setCustomGapHandler(gap_event_handler handler);
static void setSecurityAuth(bool bonding, bool mitm, bool sc);
static void setSecurityAuth(uint8_t auth);
@@ -135,8 +147,9 @@ class NimBLEDevice {
static void onReset(int reason);
static void onSync(void);
static void host_task(void* param);
static int getPower();
static bool setPower(int8_t dbm);
static int getPower(NimBLETxPowerType type = NimBLETxPowerType::All);
static bool setPower(int8_t dbm, NimBLETxPowerType type = NimBLETxPowerType::All);
static bool setDefaultPhy(uint8_t txPhyMask, uint8_t rxPhyMask);
# ifdef ESP_PLATFORM
# ifndef CONFIG_IDF_TARGET_ESP32P4
@@ -145,39 +158,39 @@ class NimBLEDevice {
# endif
# endif
# if CONFIG_BT_NIMBLE_EXT_ADV
static bool setDefaultPhy(uint8_t txPhyMask, uint8_t rxPhyMask);
# endif
# if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
# if MYNEWT_VAL(BLE_ROLE_OBSERVER)
static NimBLEScan* getScan();
# endif
# if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
# if MYNEWT_VAL(BLE_ROLE_PERIPHERAL)
static NimBLEServer* createServer();
static NimBLEServer* getServer();
# if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM)
static NimBLEL2CAPServer* createL2CAPServer();
static NimBLEL2CAPServer* getL2CAPServer();
# 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 injectPassKey(const NimBLEConnInfo& peerInfo, uint32_t pin);
# endif
# if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
# if CONFIG_BT_NIMBLE_EXT_ADV
# if MYNEWT_VAL(BLE_ROLE_BROADCASTER)
# if MYNEWT_VAL(BLE_EXT_ADV)
static NimBLEExtAdvertising* getAdvertising();
static bool startAdvertising(uint8_t instId, int duration = 0, int maxEvents = 0);
static bool stopAdvertising(uint8_t instId);
static bool stopAdvertising();
# endif
# if !CONFIG_BT_NIMBLE_EXT_ADV || defined(_DOXYGEN_)
# if !MYNEWT_VAL(BLE_EXT_ADV) || defined(_DOXYGEN_)
static NimBLEAdvertising* getAdvertising();
static bool startAdvertising(uint32_t duration = 0);
static bool stopAdvertising();
# endif
# endif
# if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
# if MYNEWT_VAL(BLE_ROLE_CENTRAL)
static NimBLEClient* createClient();
static NimBLEClient* createClient(const NimBLEAddress& peerAddress);
static bool deleteClient(NimBLEClient* pClient);
@@ -188,7 +201,7 @@ class NimBLEDevice {
static std::vector<NimBLEClient*> getConnectedClients();
# 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 int getNumBonds();
static bool isBonded(const NimBLEAddress& address);
@@ -203,81 +216,120 @@ class NimBLEDevice {
static ble_gap_event_listener m_listener;
static uint8_t m_ownAddrType;
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;
# endif
# if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
# if MYNEWT_VAL(BLE_ROLE_PERIPHERAL)
static NimBLEServer* m_pServer;
# if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM)
static NimBLEL2CAPServer* m_pL2CAPServer;
# endif
# endif
# if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
# if CONFIG_BT_NIMBLE_EXT_ADV
# if MYNEWT_VAL(BLE_ROLE_BROADCASTER)
# if MYNEWT_VAL(BLE_EXT_ADV)
static NimBLEExtAdvertising* m_bleAdvertising;
# else
static NimBLEAdvertising* m_bleAdvertising;
# endif
# endif
# if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
static std::array<NimBLEClient*, NIMBLE_MAX_CONNECTIONS> m_pClients;
# if MYNEWT_VAL(BLE_ROLE_CENTRAL)
static std::array<NimBLEClient*, MYNEWT_VAL(BLE_MAX_CONNECTIONS)> m_pClients;
# endif
# ifdef ESP_PLATFORM
# ifdef CONFIG_BTDM_BLE_SCAN_DUPL
# if CONFIG_BTDM_BLE_SCAN_DUPL || CONFIG_BT_LE_SCAN_DUPL
static uint16_t m_scanDuplicateSize;
static uint8_t m_scanFilterMode;
static uint16_t m_scanDuplicateResetTime;
# endif
# endif
# if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
# if MYNEWT_VAL(BLE_ROLE_CENTRAL)
friend class NimBLEClient;
# endif
# if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
# if MYNEWT_VAL(BLE_ROLE_OBSERVER)
friend class NimBLEScan;
# endif
# if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
# if MYNEWT_VAL(BLE_ROLE_PERIPHERAL)
friend class NimBLEServer;
friend class NimBLECharacteristic;
# endif
# if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
# if MYNEWT_VAL(BLE_ROLE_BROADCASTER)
friend class NimBLEAdvertising;
# if CONFIG_BT_NIMBLE_EXT_ADV
# if MYNEWT_VAL(BLE_EXT_ADV)
friend class NimBLEExtAdvertising;
friend class NimBLEExtAdvertisement;
# endif
# endif
};
# if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
# if MYNEWT_VAL(BLE_ROLE_CENTRAL)
# include "NimBLEClient.h"
# include "NimBLERemoteService.h"
# include "NimBLERemoteCharacteristic.h"
# include "NimBLERemoteDescriptor.h"
# endif
# if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
# if MYNEWT_VAL(BLE_ROLE_OBSERVER)
# include "NimBLEScan.h"
# endif
# if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
# if MYNEWT_VAL(BLE_ROLE_PERIPHERAL)
# include "NimBLEServer.h"
# include "NimBLEService.h"
# include "NimBLECharacteristic.h"
# include "NimBLEDescriptor.h"
# if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM)
# include "NimBLEL2CAPServer.h"
# include "NimBLEL2CAPChannel.h"
# endif
# endif
# if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
# if CONFIG_BT_NIMBLE_EXT_ADV
# if MYNEWT_VAL(BLE_ROLE_BROADCASTER)
# if MYNEWT_VAL(BLE_EXT_ADV)
# include "NimBLEExtAdvertising.h"
# else
# include "NimBLEAdvertising.h"
# endif
# endif
#endif // CONFIG_BT_ENABLED
# if MYNEWT_VAL(BLE_ROLE_CENTRAL) || MYNEWT_VAL(BLE_ROLE_PERIPHERAL)
# include "NimBLEConnInfo.h"
# endif
# include "NimBLEAddress.h"
# include "NimBLEUtils.h"
/**
* @brief Callbacks associated with a BLE device.
*/
class NimBLEDeviceCallbacks {
public:
virtual ~NimBLEDeviceCallbacks() {};
/**
* @brief Indicates an inability to perform a store operation.
* This callback should do one of two things:
* -Address the problem and return 0, indicating that the store operation
* should proceed.
* -Return nonzero to indicate that the store operation should be aborted.
* @param event Describes the store event being reported.
* BLE_STORE_EVENT_FULL; or
* BLE_STORE_EVENT_OVERFLOW
* @return 0 if the store operation should proceed;
* nonzero if the store operation should be aborted.
*/
virtual int onStoreStatus(struct ble_store_status_event* event, void* arg);
};
#endif // CONFIG_BT_NIMBLE_ENABLED
#endif // NIMBLE_CPP_DEVICE_H_

View File

@@ -1,21 +1,23 @@
/*
* NimBLEEddystoneTLM.cpp
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Created: on March 15 2020
* Author H2zero
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* Originally:
* http://www.apache.org/licenses/LICENSE-2.0
*
* BLEEddystoneTLM.cpp
*
* Created on: Mar 12, 2018
* Author: pcbreflux
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
#include "NimBLEEddystoneTLM.h"
#if CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_BROADCASTER)
# include "NimBLEEddystoneTLM.h"
# include "NimBLEUUID.h"
# include "NimBLELog.h"
@@ -213,4 +215,4 @@ void NimBLEEddystoneTLM::setTime(uint32_t tmil) {
m_eddystoneData.tmil = tmil;
} // setTime
#endif // CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_BROADCASTER
#endif // CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_BROADCASTER)

View File

@@ -1,22 +1,25 @@
/*
* NimBLEEddystoneTLM.h
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Created: on March 15 2020
* Author H2zero
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* Originally:
* http://www.apache.org/licenses/LICENSE-2.0
*
* BLEEddystoneTLM.h
*
* Created on: Mar 12, 2018
* Author: pcbreflux
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef NIMBLE_CPP_EDDYSTONETLM_H_
#define NIMBLE_CPP_EDDYSTONETLM_H_
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
#include "syscfg/syscfg.h"
#if CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_BROADCASTER)
class NimBLEUUID;
@@ -63,5 +66,5 @@ class 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_

View File

@@ -1,12 +1,22 @@
/*
* NimBLEExtAdvertising.cpp
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Created: on February 6, 2022
* Author H2zero
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER) && CONFIG_BT_NIMBLE_EXT_ADV
#include "NimBLEExtAdvertising.h"
#if CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_BROADCASTER) && MYNEWT_VAL(BLE_EXT_ADV)
# if defined(CONFIG_NIMBLE_CPP_IDF)
# include "services/gap/ble_svc_gap.h"
@@ -14,7 +24,6 @@
# include "nimble/nimble/host/services/gap/include/services/gap/ble_svc_gap.h"
# endif
# include "NimBLEExtAdvertising.h"
# include "NimBLEDevice.h"
# include "NimBLEServer.h"
# include "NimBLEUtils.h"
@@ -29,7 +38,7 @@ static const char* LOG_TAG = "NimBLEExtAdvertising";
NimBLEExtAdvertising::NimBLEExtAdvertising()
: m_deleteCallbacks{false},
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.
@@ -59,7 +68,7 @@ bool NimBLEExtAdvertising::setInstanceData(uint8_t instId, NimBLEExtAdvertisemen
adv.m_params.scan_req_notif = false;
}
# if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
# if MYNEWT_VAL(BLE_ROLE_PERIPHERAL)
NimBLEServer* pServer = NimBLEDevice::getServer();
if (pServer != nullptr) {
pServer->start(); // make sure the GATT server is ready before advertising
@@ -349,7 +358,7 @@ NimBLEExtAdvertisement::NimBLEExtAdvertisement(uint8_t priPhy, uint8_t secPhy) {
m_params.own_addr_type = NimBLEDevice::m_ownAddrType;
m_params.primary_phy = priPhy;
m_params.secondary_phy = secPhy;
m_params.tx_power = 127;
m_params.tx_power = NimBLEDevice::getPower(NimBLETxPowerType::Advertise);
} // NimBLEExtAdvertisement
/**
@@ -382,7 +391,7 @@ void NimBLEExtAdvertisement::setTxPower(int8_t dbm) {
* @param [in] enable True = connectable.
*/
void NimBLEExtAdvertisement::setConnectable(bool enable) {
# if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
# if MYNEWT_VAL(BLE_ROLE_PERIPHERAL)
m_params.connectable = enable;
# endif
} // setConnectable
@@ -522,7 +531,7 @@ void NimBLEExtAdvertisement::clearData() {
* @details This will completely replace any data that was previously set.
*/
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;
}
@@ -537,7 +546,7 @@ bool NimBLEExtAdvertisement::setData(const uint8_t* data, size_t length) {
* @return True if successful, false if the data is too large.
*/
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;
}
@@ -551,7 +560,7 @@ bool NimBLEExtAdvertisement::addData(const uint8_t* data, size_t length) {
* @return True if successful, false if the data is too large.
*/
bool NimBLEExtAdvertisement::addData(const std::string& data) {
if (m_payload.size() + data.length() > CONFIG_BT_NIMBLE_MAX_EXT_ADV_DATA_LEN) {
if (m_payload.size() + data.length() > MYNEWT_VAL(BLE_EXT_ADV_MAX_SIZE)) {
return false;
}
@@ -607,6 +616,11 @@ bool NimBLEExtAdvertisement::setFlags(uint8_t flag) {
* @return True if successful.
*/
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];
header[0] = length + 1;
header[1] = BLE_HS_ADV_TYPE_MFG_DATA;
@@ -643,6 +657,11 @@ bool NimBLEExtAdvertisement::setManufacturerData(const std::vector<uint8_t>& dat
* @return True if successful.
*/
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];
header[0] = uri.length() + 1;
header[1] = BLE_HS_ADV_TYPE_URI;
@@ -661,6 +680,11 @@ bool NimBLEExtAdvertisement::setURI(const std::string& uri) {
* @return True if successful.
*/
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];
header[0] = name.length() + 1;
header[1] = isComplete ? BLE_HS_ADV_TYPE_COMP_NAME : BLE_HS_ADV_TYPE_INCOMP_NAME;
@@ -691,6 +715,7 @@ bool NimBLEExtAdvertisement::addServiceUUID(const NimBLEUUID& serviceUUID) {
type = BLE_HS_ADV_TYPE_COMP_UUIDS128;
break;
default:
NIMBLE_LOGE(LOG_TAG, "Cannot add UUID, invalid size!");
return false;
}
@@ -700,11 +725,12 @@ bool NimBLEExtAdvertisement::addServiceUUID(const NimBLEUUID& serviceUUID) {
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;
}
uint8_t data[31];
uint8_t data[BLE_HS_ADV_MAX_SZ];
const uint8_t* uuid = serviceUUID.getValue();
if (dataLoc == -1) {
data[0] = 1 + bytes;
@@ -746,6 +772,7 @@ bool NimBLEExtAdvertisement::removeServiceUUID(const NimBLEUUID& serviceUUID) {
type = BLE_HS_ADV_TYPE_COMP_UUIDS128;
break;
default:
NIMBLE_LOGE(LOG_TAG, "Cannot remove UUID, invalid size!");
return false;
}
@@ -755,7 +782,7 @@ bool NimBLEExtAdvertisement::removeServiceUUID(const NimBLEUUID& serviceUUID) {
}
int uuidLoc = -1;
for (int i = dataLoc + 2; i < m_payload.size(); i += bytes) {
for (size_t i = dataLoc + 2; i < m_payload.size(); i += bytes) {
if (memcmp(&m_payload[i], serviceUUID.getValue(), bytes) == 0) {
uuidLoc = i;
break;
@@ -868,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;
break;
default:
NIMBLE_LOGE(LOG_TAG, "Cannot set services, invalid size!");
return false;
}
@@ -904,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) {
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;
}
@@ -922,6 +954,7 @@ bool NimBLEExtAdvertisement::setServiceData(const NimBLEUUID& uuid, const uint8_
type = BLE_HS_ADV_TYPE_SVC_DATA_UUID128;
break;
default:
NIMBLE_LOGE(LOG_TAG, "Cannot set service data, invalid size!");
return false;
}
@@ -999,8 +1032,22 @@ bool NimBLEExtAdvertisement::setPreferredParams(uint16_t minInterval, uint16_t m
/**
* @brief Adds Tx power level to the advertisement data.
*/
void NimBLEExtAdvertisement::addTxPower() {
bool NimBLEExtAdvertisement::addTxPower() {
if (m_params.legacy_pdu) {
m_params.include_tx_power = 0;
uint8_t data[3];
data[0] = BLE_HS_ADV_TX_PWR_LVL_LEN + 1;
data[1] = BLE_HS_ADV_TYPE_TX_PWR_LVL;
# ifndef CONFIG_IDF_TARGET_ESP32P4
data[2] = NimBLEDevice::getPower(NimBLETxPowerType::Advertise);
# else
data[2] = 0;
# endif
return addData(data, 3);
}
m_params.include_tx_power = 1;
return true;
} // addTxPower
/**
@@ -1009,7 +1056,7 @@ void NimBLEExtAdvertisement::addTxPower() {
* @return -1 if the data is not found, otherwise the index of the data in the payload.
*/
int NimBLEExtAdvertisement::getDataLocation(uint8_t type) const {
int index = 0;
size_t index = 0;
while (index < m_payload.size()) {
if (m_payload[index + 1] == type) {
return index;
@@ -1062,4 +1109,4 @@ std::string NimBLEExtAdvertisement::toString() const {
return str;
} // 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)

View File

@@ -1,15 +1,25 @@
/*
* NimBLEExtAdvertising.h
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Created: on February 6, 2022
* Author H2zero
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef NIMBLE_CPP_EXTADVERTISING_H_
#define NIMBLE_CPP_EXTADVERTISING_H_
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER) && CONFIG_BT_NIMBLE_EXT_ADV
#include "syscfg/syscfg.h"
#if CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_BROADCASTER) && MYNEWT_VAL(BLE_EXT_ADV)
# if defined(CONFIG_NIMBLE_CPP_IDF)
# include "host/ble_gap.h"
@@ -36,34 +46,33 @@ class NimBLEUUID;
class NimBLEExtAdvertisement {
public:
NimBLEExtAdvertisement(uint8_t priPhy = BLE_HCI_LE_PHY_1M, uint8_t secPhy = BLE_HCI_LE_PHY_1M);
bool setAppearance(uint16_t appearance);
bool addServiceUUID(const NimBLEUUID& serviceUUID);
bool addServiceUUID(const char* serviceUUID);
bool removeServiceUUID(const NimBLEUUID& serviceUUID);
bool removeServiceUUID(const char* serviceUUID);
bool removeServices();
bool setCompleteServices(const NimBLEUUID& uuid);
bool setCompleteServices16(const std::vector<NimBLEUUID>& uuids);
bool setCompleteServices32(const std::vector<NimBLEUUID>& uuids);
bool setFlags(uint8_t flag);
bool setManufacturerData(const uint8_t* data, size_t length);
bool setManufacturerData(const std::string& data);
bool setManufacturerData(const std::vector<uint8_t>& data);
bool setURI(const std::string& uri);
bool setName(const std::string& name, bool isComplete = true);
bool setPartialServices(const NimBLEUUID& uuid);
bool setPartialServices16(const std::vector<NimBLEUUID>& uuids);
bool setPartialServices32(const std::vector<NimBLEUUID>& uuids);
bool setServiceData(const NimBLEUUID& uuid, const uint8_t* data, size_t length);
bool setServiceData(const NimBLEUUID& uuid, const std::string& data);
bool setServiceData(const NimBLEUUID& uuid, const std::vector<uint8_t>& data);
bool setShortName(const std::string& name);
bool setData(const uint8_t* data, size_t length);
bool addData(const uint8_t* data, size_t length);
bool addData(const std::string& data);
bool setPreferredParams(uint16_t min, uint16_t max);
void addTxPower();
bool setAppearance(uint16_t appearance);
bool addServiceUUID(const NimBLEUUID& serviceUUID);
bool addServiceUUID(const char* serviceUUID);
bool removeServiceUUID(const NimBLEUUID& serviceUUID);
bool removeServiceUUID(const char* serviceUUID);
bool removeServices();
bool setCompleteServices(const NimBLEUUID& uuid);
bool setCompleteServices16(const std::vector<NimBLEUUID>& uuids);
bool setCompleteServices32(const std::vector<NimBLEUUID>& uuids);
bool setFlags(uint8_t flag);
bool setManufacturerData(const uint8_t* data, size_t length);
bool setManufacturerData(const std::string& data);
bool setManufacturerData(const std::vector<uint8_t>& data);
bool setURI(const std::string& uri);
bool setName(const std::string& name, bool isComplete = true);
bool setPartialServices(const NimBLEUUID& uuid);
bool setPartialServices16(const std::vector<NimBLEUUID>& uuids);
bool setPartialServices32(const std::vector<NimBLEUUID>& uuids);
bool setServiceData(const NimBLEUUID& uuid, const uint8_t* data, size_t length);
bool setServiceData(const NimBLEUUID& uuid, const std::string& data);
bool setServiceData(const NimBLEUUID& uuid, const std::vector<uint8_t>& data);
bool setShortName(const std::string& name);
bool setData(const uint8_t* data, size_t length);
bool addData(const uint8_t* data, size_t length);
bool addData(const std::string& data);
bool setPreferredParams(uint16_t min, uint16_t max);
bool addTxPower();
void setLegacyAdvertising(bool enable);
void setConnectable(bool enable);
void setScannable(bool enable);
@@ -150,5 +159,5 @@ class NimBLEExtAdvertisingCallbacks {
virtual void onScanRequest(NimBLEExtAdvertising* pAdv, uint8_t instId, NimBLEAddress addr);
}; // NimBLEExtAdvertisingCallbacks
#endif // CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_BROADCASTER && CONFIG_BT_NIMBLE_EXT_ADV
#endif // CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_BROADCASTER) && MYNEWT_VAL(BLE_EXT_ADV)
#endif // NIMBLE_CPP_EXTADVERTISING_H_

View File

@@ -1,21 +1,23 @@
/*
* NimBLEHIDDevice.cpp
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Created: on Oct 06 2020
* Author wakwak-koba
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* Originally:
* http://www.apache.org/licenses/LICENSE-2.0
*
* BLEHIDDevice.cpp
*
* Created on: Jan 03, 2018
* Author: chegewara
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#include "NimBLEHIDDevice.h"
#if CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_BROADCASTER) && MYNEWT_VAL(BLE_ROLE_PERIPHERAL)
# include "NimBLEHIDDevice.h"
# include "NimBLEServer.h"
# include "NimBLEService.h"
# include "NimBLE2904.h"
@@ -146,14 +148,35 @@ void NimBLEHIDDevice::setBatteryLevel(uint8_t level, bool notify) {
}
} // 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.
* @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.
* @param [in] reportId Input report ID, the same as in report map for input object related to the characteristic.
* @return NimBLECharacteristic* A pointer to the input report characteristic.
* Store this value to avoid computational overhead.
* @details This will create the characteristic if not already created.
*/
NimBLECharacteristic* NimBLEHIDDevice::getInputReport(uint8_t reportId) {
NimBLECharacteristic* inputReportChr = m_hidSvc->getCharacteristic(inputReportChrUuid);
NimBLECharacteristic* inputReportChr = locateReportCharacteristicByIdAndType(reportId, 0x01);
if (inputReportChr == nullptr) {
inputReportChr =
m_hidSvc->createCharacteristic(inputReportChrUuid,
@@ -171,11 +194,12 @@ NimBLECharacteristic* NimBLEHIDDevice::getInputReport(uint8_t reportId) {
/**
* @brief Get the output report characteristic.
* @param [in] reportId Output report ID, the same as in report map for output object related to the characteristic.
* @return 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.
*/
NimBLECharacteristic* NimBLEHIDDevice::getOutputReport(uint8_t reportId) {
NimBLECharacteristic* outputReportChr = m_hidSvc->getCharacteristic(inputReportChrUuid);
NimBLECharacteristic* outputReportChr = locateReportCharacteristicByIdAndType(reportId, 0x02);
if (outputReportChr == nullptr) {
outputReportChr =
m_hidSvc->createCharacteristic(inputReportChrUuid,
@@ -184,7 +208,6 @@ NimBLECharacteristic* NimBLEHIDDevice::getOutputReport(uint8_t reportId) {
NimBLEDescriptor* outputReportDsc = outputReportChr->createDescriptor(
featureReportDscUuid,
NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::READ_ENC | NIMBLE_PROPERTY::WRITE_ENC);
uint8_t desc1_val[] = {reportId, 0x02};
outputReportDsc->setValue(desc1_val, 2);
}
@@ -195,11 +218,12 @@ NimBLECharacteristic* NimBLEHIDDevice::getOutputReport(uint8_t reportId) {
/**
* @brief Get the feature report characteristic.
* @param [in] reportId Feature report ID, the same as in report map for feature object related to the characteristic.
* @return 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.
*/
NimBLECharacteristic* NimBLEHIDDevice::getFeatureReport(uint8_t reportId) {
NimBLECharacteristic* featureReportChr = m_hidSvc->getCharacteristic(inputReportChrUuid);
NimBLECharacteristic* featureReportChr = locateReportCharacteristicByIdAndType(reportId, 0x03);
if (featureReportChr == nullptr) {
featureReportChr = m_hidSvc->createCharacteristic(
inputReportChrUuid,
@@ -316,4 +340,4 @@ NimBLEService* NimBLEHIDDevice::getBatteryService() {
return m_batterySvc;
} // getBatteryService
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL */
#endif // CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_PERIPHERAL)

View File

@@ -1,22 +1,25 @@
/*
* NimBLEHIDDevice.h
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Created: on Oct 06 2020
* Author wakwak-koba
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* Originally:
* http://www.apache.org/licenses/LICENSE-2.0
*
* BLEHIDDevice.h
*
* Created on: Jan 03, 2018
* Author: chegewara
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef NIMBLE_CPP_HIDDEVICE_H_
#define NIMBLE_CPP_HIDDEVICE_H_
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#include "syscfg/syscfg.h"
#if CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_BROADCASTER) && MYNEWT_VAL(BLE_ROLE_PERIPHERAL)
# include <stdint.h>
# include <string>
@@ -78,7 +81,9 @@ class NimBLEHIDDevice {
NimBLECharacteristic* m_hidControlChr{nullptr}; // 0x2a4c
NimBLECharacteristic* m_protocolModeChr{nullptr}; // 0x2a4e
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_

314
src/NimBLEL2CAPChannel.cpp Normal file
View 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
View 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
View 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
View 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_

View File

@@ -1,15 +1,25 @@
/*
* NimBLELocalAttribute.cpp
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Created: on July 28 2024
* Author H2zero
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef NIMBLE_CPP_LOCAL_ATTRIBUTE_H_
#define NIMBLE_CPP_LOCAL_ATTRIBUTE_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)
# include "NimBLEAttribute.h"
@@ -44,5 +54,5 @@ class NimBLELocalAttribute : public NimBLEAttribute {
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_

View File

@@ -1,15 +1,25 @@
/*
* NimBLELocalValueAttribute.cpp
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Created: on July 28 2024
* Author H2zero
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef NIMBLE_LOCAL_VALUE_ATTRIBUTE_H_
#define NIMBLE_LOCAL_VALUE_ATTRIBUTE_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)
# if defined(CONFIG_NIMBLE_CPP_IDF)
# include "host/ble_hs.h"
@@ -38,30 +48,18 @@ typedef enum {
} NIMBLE_PROPERTY;
# include "NimBLELocalAttribute.h"
# include "NimBLEValueAttribute.h"
# include "NimBLEAttValue.h"
# include <vector>
class NimBLEConnInfo;
class NimBLELocalValueAttribute : public NimBLELocalAttribute {
class NimBLELocalValueAttribute : public NimBLELocalAttribute, public NimBLEValueAttribute {
public:
/**
* @brief Get the properties of the attribute.
*/
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.
* @param [in] data The data to set the value to.
@@ -90,19 +88,6 @@ class NimBLELocalValueAttribute : public NimBLELocalAttribute {
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>(&timestamp, skipSizeCheck);</tt>
*/
template <typename T>
T getValue(time_t* timestamp = nullptr, bool skipSizeCheck = false) const {
return m_value.getValue<T>(timestamp, skipSizeCheck);
}
protected:
friend class NimBLEServer;
@@ -116,9 +101,8 @@ class NimBLELocalValueAttribute : public NimBLELocalAttribute {
NimBLELocalValueAttribute(const NimBLEUUID& uuid,
uint16_t handle,
uint16_t maxLen,
uint16_t initLen = CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH)
: NimBLELocalAttribute(uuid, handle), m_value(initLen, maxLen) {}
uint16_t initLen = MYNEWT_VAL(NIMBLE_CPP_ATT_VALUE_INIT_LENGTH))
: NimBLELocalAttribute(uuid, handle), NimBLEValueAttribute(maxLen, initLen) {}
/**
* @brief Destroy the NimBLELocalValueAttribute object.
*/
@@ -153,9 +137,8 @@ class NimBLELocalValueAttribute : public NimBLELocalAttribute {
*/
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_

View File

@@ -1,81 +1,184 @@
/*
* NimBLELog.h
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Created: on Feb 24 2020
* Author H2zero
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef MAIN_NIMBLELOG_H_
#define MAIN_NIMBLELOG_H_
#include "nimconfig.h"
#ifndef NIMBLE_CPP_LOG_H_
#define NIMBLE_CPP_LOG_H_
#if defined(CONFIG_BT_ENABLED)
#include "syscfg/syscfg.h"
#if CONFIG_BT_NIMBLE_ENABLED
#if (0) // using esp-idf
# 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)
# include "esp_log.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, ...) do { \
if (CONFIG_NIMBLE_CPP_LOG_LEVEL >= level) \
ESP_LOG_LEVEL_LOCAL(level, tag, format, ##__VA_ARGS__); \
} while(0)
# define NIMBLE_LOGD(tag, format, ...) \
NIMBLE_CPP_LOG_PRINT(ESP_LOG_DEBUG, tag, format, ##__VA_ARGS__)
# define NIMBLE_LOGI(tag, format, ...) \
NIMBLE_CPP_LOG_PRINT(ESP_LOG_INFO, tag, format, ##__VA_ARGS__)
# define NIMBLE_LOGW(tag, format, ...) \
NIMBLE_CPP_LOG_PRINT(ESP_LOG_WARN, tag, format, ##__VA_ARGS__)
# define NIMBLE_LOGE(tag, format, ...) \
NIMBLE_CPP_LOG_PRINT(ESP_LOG_ERROR, tag, format, ##__VA_ARGS__)
#else // using Arduino
//# include "nimble/porting/nimble/include/syscfg/syscfg.h"
//# include "nimble/console/console.h"
# include "syscfg/syscfg.h"
# include "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
# if defined(CONFIG_NIMBLE_CPP_LOG_OVERRIDE_COLOR)
# if CONFIG_LOG_COLORS
# if defined(CONFIG_NIMBLE_CPP_LOG_OVERRIDE_COLOR_DEBUG_BLACK)
# define NIMBLE_CPP_LOG_COLOR_D LOG_COLOR(LOG_COLOR_BLACK)
# elif defined(CONFIG_NIMBLE_CPP_LOG_OVERRIDE_COLOR_DEBUG_RED)
# define NIMBLE_CPP_LOG_COLOR_D LOG_COLOR(LOG_COLOR_RED)
# elif defined(CONFIG_NIMBLE_CPP_LOG_OVERRIDE_COLOR_DEBUG_GREEN)
# define NIMBLE_CPP_LOG_COLOR_D LOG_COLOR(LOG_COLOR_GREEN)
# elif defined(CONFIG_NIMBLE_CPP_LOG_OVERRIDE_COLOR_DEBUG_YELLOW)
# define NIMBLE_CPP_LOG_COLOR_D LOG_COLOR(LOG_COLOR_BROWN)
# elif defined(CONFIG_NIMBLE_CPP_LOG_OVERRIDE_COLOR_DEBUG_BLUE)
# define NIMBLE_CPP_LOG_COLOR_D LOG_COLOR(LOG_COLOR_BLUE)
# elif defined(CONFIG_NIMBLE_CPP_LOG_OVERRIDE_COLOR_DEBUG_PURPLE)
# define NIMBLE_CPP_LOG_COLOR_D LOG_COLOR(LOG_COLOR_PURPLE)
# elif defined(CONFIG_NIMBLE_CPP_LOG_OVERRIDE_COLOR_DEBUG_CYAN)
# define NIMBLE_CPP_LOG_COLOR_D LOG_COLOR(LOG_COLOR_CYAN)
# else
# define CONFIG_NIMBLE_CPP_LOG_LEVEL 0
# define NIMBLE_CPP_LOG_COLOR_D
# endif
# endif
# if CONFIG_NIMBLE_CPP_LOG_LEVEL >= 4
# define NIMBLE_LOGD( tag, format, ... ) console_printf("D %s: " format "\n", tag, ##__VA_ARGS__)
# if defined(CONFIG_NIMBLE_CPP_LOG_OVERRIDE_COLOR_INFO_BLACK)
# define NIMBLE_CPP_LOG_COLOR_I LOG_COLOR(LOG_COLOR_BLACK)
# elif defined(CONFIG_NIMBLE_CPP_LOG_OVERRIDE_COLOR_INFO_RED)
# define NIMBLE_CPP_LOG_COLOR_I LOG_COLOR(LOG_COLOR_RED)
# elif defined(CONFIG_NIMBLE_CPP_LOG_OVERRIDE_COLOR_INFO_GREEN)
# define NIMBLE_CPP_LOG_COLOR_I LOG_COLOR(LOG_COLOR_GREEN)
# elif defined(CONFIG_NIMBLE_CPP_LOG_OVERRIDE_COLOR_INFO_YELLOW)
# define NIMBLE_CPP_LOG_COLOR_I LOG_COLOR(LOG_COLOR_BROWN)
# elif defined(CONFIG_NIMBLE_CPP_LOG_OVERRIDE_COLOR_INFO_BLUE)
# define NIMBLE_CPP_LOG_COLOR_I LOG_COLOR(LOG_COLOR_BLUE)
# elif defined(CONFIG_NIMBLE_CPP_LOG_OVERRIDE_COLOR_INFO_PURPLE)
# define NIMBLE_CPP_LOG_COLOR_I LOG_COLOR(LOG_COLOR_PURPLE)
# elif defined(CONFIG_NIMBLE_CPP_LOG_OVERRIDE_COLOR_INFO_CYAN)
# define NIMBLE_CPP_LOG_COLOR_I LOG_COLOR(LOG_COLOR_CYAN)
# else
# define NIMBLE_CPP_LOG_COLOR_I
# endif
# 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)
# else
# define NIMBLE_LOGD( tag, format, ... ) (void)tag
# endif
# 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)
# if CONFIG_NIMBLE_CPP_LOG_LEVEL >= 3
# define NIMBLE_LOGI( tag, format, ... ) console_printf("I %s: " format "\n", tag, ##__VA_ARGS__)
# endif /* CONFIG_NIMBLE_CPP_LOG_OVERRIDE_COLOR */
# define NIMBLE_LOGD(tag, format, ...) NIMBLE_CPP_LOG_PRINT(ESP_LOG_DEBUG, tag, format, ##__VA_ARGS__)
# define NIMBLE_LOGI(tag, format, ...) NIMBLE_CPP_LOG_PRINT(ESP_LOG_INFO, tag, format, ##__VA_ARGS__)
# define NIMBLE_LOGW(tag, format, ...) NIMBLE_CPP_LOG_PRINT(ESP_LOG_WARN, tag, format, ##__VA_ARGS__)
# define NIMBLE_LOGE(tag, format, ...) NIMBLE_CPP_LOG_PRINT(ESP_LOG_ERROR, tag, format, ##__VA_ARGS__)
# else
# include "nimble/console/console.h"
# if MYNEWT_VAL(NIMBLE_CPP_LOG_LEVEL) >= 4
# define NIMBLE_LOGD(tag, format, ...) console_printf("D %s: " format "\n", tag, ##__VA_ARGS__)
# else
# define NIMBLE_LOGI( tag, format, ... ) (void)tag
# define NIMBLE_LOGD(tag, format, ...) (void)tag
# endif
# if CONFIG_NIMBLE_CPP_LOG_LEVEL >= 2
# define NIMBLE_LOGW( tag, format, ... ) console_printf("W %s: " format "\n", tag, ##__VA_ARGS__)
# if MYNEWT_VAL(NIMBLE_CPP_LOG_LEVEL) >= 3
# define NIMBLE_LOGI(tag, format, ...) console_printf("I %s: " format "\n", tag, ##__VA_ARGS__)
# else
# define NIMBLE_LOGW( tag, format, ... ) (void)tag
# define NIMBLE_LOGI(tag, format, ...) (void)tag
# endif
# if CONFIG_NIMBLE_CPP_LOG_LEVEL >= 1
# define NIMBLE_LOGE( tag, format, ... ) console_printf("E %s: " format "\n", tag, ##__VA_ARGS__)
# if MYNEWT_VAL(NIMBLE_CPP_LOG_LEVEL) >= 2
# define NIMBLE_LOGW(tag, format, ...) console_printf("W %s: " format "\n", tag, ##__VA_ARGS__)
# else
# define NIMBLE_LOGE( tag, format, ... ) (void)tag
# define NIMBLE_LOGW(tag, format, ...) (void)tag
# endif
#endif /* CONFIG_NIMBLE_CPP_IDF */
# if MYNEWT_VAL(NIMBLE_CPP_LOG_LEVEL) >= 1
# define NIMBLE_LOGE(tag, format, ...) console_printf("E %s: " format "\n", tag, ##__VA_ARGS__)
# else
# define NIMBLE_LOGE(tag, format, ...) (void)tag
# endif
#define NIMBLE_LOGC( tag, format, ... ) console_printf("CRIT %s: " format "\n", tag, ##__VA_ARGS__)
# endif /* CONFIG_NIMBLE_CPP_IDF */
#endif /* CONFIG_BT_ENABLED */
#endif /* MAIN_NIMBLELOG_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_ */

View File

@@ -1,21 +1,23 @@
/*
* NimBLERemoteCharacteristic.cpp
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Created: on Jan 27 2020
* Author H2zero
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* Originally:
* http://www.apache.org/licenses/LICENSE-2.0
*
* BLERemoteCharacteristic.cpp
*
* Created on: Mar 16, 2017
* Author: kolban
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
#include "NimBLERemoteCharacteristic.h"
#if CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_CENTRAL)
# include "NimBLERemoteCharacteristic.h"
# include "NimBLERemoteDescriptor.h"
# include "NimBLERemoteService.h"
# include "NimBLEClient.h"
@@ -24,17 +26,18 @@
# include <climits>
typedef struct {
const NimBLEUUID* uuid;
void* task_data;
} desc_filter_t;
struct NimBLEDescriptorFilter {
NimBLERemoteDescriptor* dsc;
const NimBLEUUID* uuid;
void* taskData;
};
static const char* LOG_TAG = "NimBLERemoteCharacteristic";
/**
* @brief Constructor.
* @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 {
* uint16_t def_handle;
* uint16_t val_handle;
@@ -60,71 +63,73 @@ NimBLERemoteCharacteristic::~NimBLERemoteCharacteristic() {
* @brief Callback used by the API when a descriptor is discovered or search complete.
*/
int NimBLERemoteCharacteristic::descriptorDiscCB(
uint16_t conn_handle, const ble_gatt_error* error, uint16_t chr_val_handle, const ble_gatt_dsc* dsc, void* arg) {
int rc = error->status;
uint16_t connHandle, const ble_gatt_error* error, uint16_t chrHandle, const ble_gatt_dsc* dsc, void* arg) {
int rc = error->status;
auto filter = (NimBLEDescriptorFilter*)arg;
auto pTaskData = (NimBLETaskData*)filter->taskData;
const auto pChr = (NimBLERemoteCharacteristic*)pTaskData->m_pInstance;
const auto uuid = filter->uuid; // UUID to filter for
NIMBLE_LOGD(LOG_TAG, "Descriptor Discovery >> status: %d handle: %d", rc, (rc == 0) ? dsc->handle : -1);
auto filter = (desc_filter_t*)arg;
auto pTaskData = (NimBLETaskData*)filter->task_data;
const auto pChr = (NimBLERemoteCharacteristic*)pTaskData->m_pInstance;
const NimBLEUUID* uuidFilter = filter->uuid;
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
}
}
// Results for chrHandle added until rc != 0
// Must find specified UUID if filter is used
if (rc == 0 && pChr->getHandle() == chrHandle && (!uuid || 0 == ble_uuid_cmp(uuid->getBase(), &dsc->uuid.u))) {
// Return BLE_HS_EDONE if the descriptor was found, stop the search
pChr->m_vDescriptors.push_back(new NimBLERemoteDescriptor(pChr, dsc));
rc = !!uuid * BLE_HS_EDONE;
}
NimBLEUtils::taskRelease(*pTaskData, rc);
NIMBLE_LOGD(LOG_TAG, "<< Descriptor Discovery");
if (rc != 0) {
NimBLEUtils::taskRelease(*pTaskData, rc);
NIMBLE_LOGD(LOG_TAG, "<< Descriptor Discovery");
}
return rc;
}
/**
* @brief Populate the descriptors (if any) for this characteristic.
* @param [in] the end handle of the characteristic, or the service, whichever comes first.
* @param [in] pFilter Pointer to a filter containing pointers to descriptor, UUID, and task data.
* @return True if successfully retrieved, success = BLE_HS_EDONE.
*/
bool NimBLERemoteCharacteristic::retrieveDescriptors(const NimBLEUUID* uuidFilter) const {
bool NimBLERemoteCharacteristic::retrieveDescriptors(NimBLEDescriptorFilter* pFilter) const {
NIMBLE_LOGD(LOG_TAG, ">> retrieveDescriptors() for characteristic: %s", getUUID().toString().c_str());
NimBLETaskData taskData(const_cast<NimBLERemoteCharacteristic*>(this));
desc_filter_t filter = {uuidFilter, &taskData};
// If this is the last handle then there are no descriptors
if (getHandle() == getRemoteService()->getEndHandle()) {
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(),
getHandle(),
getRemoteService()->getEndHandle(),
NimBLERemoteCharacteristic::descriptorDiscCB,
&filter);
pFilter);
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "ble_gattc_disc_all_dscs: rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc));
return false;
}
auto prevDscCount = m_vDescriptors.size();
NimBLEUtils::taskWait(taskData, BLE_NPL_TIME_FOREVER);
rc = taskData.m_flags;
if (rc == 0 || rc == BLE_HS_EDONE) {
NIMBLE_LOGD(LOG_TAG, "<< retrieveDescriptors(): found %d descriptors.", m_vDescriptors.size());
return true;
rc = ((NimBLETaskData*)pFilter->taskData)->m_flags;
if (rc != BLE_HS_EDONE) {
NIMBLE_LOGE(LOG_TAG, "<< retrieveDescriptors(): failed: rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc));
return false;
}
NIMBLE_LOGE(LOG_TAG, "<< retrieveDescriptors(): failed: rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc));
return false;
if (m_vDescriptors.size() > prevDscCount) {
pFilter->dsc = m_vDescriptors.back();
}
NIMBLE_LOGD(LOG_TAG, "<< retrieveDescriptors(): found %d descriptors.", m_vDescriptors.size() - prevDscCount);
return true;
} // retrieveDescriptors
/**
@@ -134,51 +139,38 @@ bool NimBLERemoteCharacteristic::retrieveDescriptors(const NimBLEUUID* uuidFilte
*/
NimBLERemoteDescriptor* NimBLERemoteCharacteristic::getDescriptor(const NimBLEUUID& uuid) const {
NIMBLE_LOGD(LOG_TAG, ">> getDescriptor: uuid: %s", uuid.toString().c_str());
NimBLERemoteDescriptor* pDsc = nullptr;
size_t prev_size = m_vDescriptors.size();
NimBLEUUID uuidTmp{uuid};
NimBLETaskData taskData(const_cast<NimBLERemoteCharacteristic*>(this));
NimBLEDescriptorFilter filter{nullptr, &uuidTmp, &taskData};
for (const auto& it : m_vDescriptors) {
if (it->getUUID() == uuid) {
pDsc = it;
for (const auto& dsc : m_vDescriptors) {
if (dsc->getUUID() == uuid) {
filter.dsc = dsc;
goto Done;
}
}
if (retrieveDescriptors(&uuid)) {
if (m_vDescriptors.size() > prev_size) {
pDsc = m_vDescriptors.back();
goto Done;
}
if (!retrieveDescriptors(&filter) || filter.dsc) {
goto Done;
}
// If the request was successful but 16/32 bit uuid not found
// try again with the 128 bit uuid.
if (uuid.bitSize() == BLE_UUID_TYPE_16 || uuid.bitSize() == BLE_UUID_TYPE_32) {
NimBLEUUID uuid128(uuid);
uuid128.to128();
if (retrieveDescriptors(&uuid128)) {
if (m_vDescriptors.size() > prev_size) {
pDsc = m_vDescriptors.back();
}
}
} else {
// If the request was successful but the 128 bit uuid not found
// try again with the 16 bit uuid.
NimBLEUUID uuid16(uuid);
uuid16.to16();
// if the uuid was 128 bit but not of the BLE base type this check will fail
if (uuid16.bitSize() == BLE_UUID_TYPE_16) {
if (retrieveDescriptors(&uuid16)) {
if (m_vDescriptors.size() > prev_size) {
pDsc = m_vDescriptors.back();
}
}
}
}
// Try again with 128 bit uuid if request succeeded but no descriptor found.
if (uuid.bitSize() != BLE_UUID_TYPE_128) {
uuidTmp.to128();
retrieveDescriptors(&filter);
goto Done;
}
// If the uuid was 128 bit, try again with 16 bit uuid.
uuidTmp.to16();
if (uuidTmp.bitSize() == BLE_UUID_TYPE_16) {
filter.uuid = &uuidTmp;
retrieveDescriptors(&filter);
}
Done:
NIMBLE_LOGD(LOG_TAG, "<< getDescriptor: %sfound", pDsc ? "" : "not ");
return pDsc;
NIMBLE_LOGD(LOG_TAG, "<< getDescriptor: %sfound", filter.dsc ? "" : "not ");
return filter.dsc;
} // getDescriptor
/**
@@ -307,7 +299,7 @@ size_t NimBLERemoteCharacteristic::deleteDescriptor(const NimBLEUUID& uuid) cons
* @return True if supported.
*/
bool NimBLERemoteCharacteristic::canBroadcast() const {
return (m_properties & BLE_GATT_CHR_PROP_BROADCAST) != 0;
return (m_properties & BLE_GATT_CHR_PROP_BROADCAST);
};
/**
@@ -395,4 +387,4 @@ NimBLEClient* NimBLERemoteCharacteristic::getClient() const {
return getRemoteService()->getClient();
} // getClient
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL */
#endif // CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_CENTRAL)

View File

@@ -1,29 +1,34 @@
/*
* NimBLERemoteCharacteristic.h
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Created: on Jan 27 2020
* Author H2zero
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* Originally:
* http://www.apache.org/licenses/LICENSE-2.0
*
* BLERemoteCharacteristic.h
*
* Created on: Jul 8, 2017
* Author: kolban
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef NIMBLE_CPP_REMOTE_CHARACTERISTIC_H_
#define NIMBLE_CPP_REMOTE_CHARACTERISTIC_H_
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
#include "syscfg/syscfg.h"
#if CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_CENTRAL)
# include "NimBLERemoteValueAttribute.h"
# include <vector>
# include <functional>
class NimBLEUUID;
class NimBLERemoteService;
class NimBLERemoteDescriptor;
struct NimBLEDescriptorFilter;
/**
* @brief A model of a remote BLE characteristic.
@@ -62,10 +67,10 @@ class NimBLERemoteCharacteristic : public NimBLERemoteValueAttribute {
~NimBLERemoteCharacteristic();
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(
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};
uint8_t m_properties{0};
@@ -74,5 +79,5 @@ class NimBLERemoteCharacteristic : public NimBLERemoteValueAttribute {
}; // 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_ */

View File

@@ -1,21 +1,23 @@
/*
* NimBLERemoteDescriptor.cpp
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Created: on Jan 27 2020
* Author H2zero
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* Originally:
* http://www.apache.org/licenses/LICENSE-2.0
*
* BLERemoteDescriptor.cpp
*
* Created on: Jul 8, 2017
* Author: kolban
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
#include "NimBLERemoteDescriptor.h"
#if CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_CENTRAL)
# include "NimBLERemoteDescriptor.h"
# include "NimBLERemoteCharacteristic.h"
/**
@@ -25,7 +27,8 @@
*/
NimBLERemoteDescriptor::NimBLERemoteDescriptor(const NimBLERemoteCharacteristic* pRemoteCharacteristic,
const ble_gatt_dsc* dsc)
: NimBLERemoteValueAttribute{dsc->uuid, dsc->handle}, m_pRemoteCharacteristic{pRemoteCharacteristic} {} // NimBLERemoteDescriptor
: NimBLERemoteValueAttribute{dsc->uuid, dsc->handle},
m_pRemoteCharacteristic{pRemoteCharacteristic} {} // NimBLERemoteDescriptor
/**
* @brief Get the characteristic that owns this descriptor.
@@ -53,4 +56,4 @@ NimBLEClient* NimBLERemoteDescriptor::getClient() const {
return m_pRemoteCharacteristic->getClient();
}
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL */
#endif // CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_CENTRAL)

View File

@@ -1,22 +1,25 @@
/*
* NimBLERemoteDescriptor.h
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Created: on Jan 27 2020
* Author H2zero
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* Originally:
* http://www.apache.org/licenses/LICENSE-2.0
*
* BLERemoteDescriptor.h
*
* Created on: Jul 8, 2017
* Author: kolban
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef NIMBLE_CPP_REMOTE_DESCRIPTOR_H_
#define NIMBLE_CPP_REMOTE_DESCRIPTOR_H_
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
#include "syscfg/syscfg.h"
#if CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_CENTRAL)
# include "NimBLERemoteValueAttribute.h"
@@ -41,5 +44,5 @@ class NimBLERemoteDescriptor : public NimBLERemoteValueAttribute {
const NimBLERemoteCharacteristic* m_pRemoteCharacteristic;
};
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL */
#endif /* NIMBLE_CPP_REMOTE_DESCRIPTOR_H_ */
#endif // CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_CENTRAL)
#endif // NIMBLE_CPP_REMOTE_DESCRIPTOR_H_

View File

@@ -1,21 +1,23 @@
/*
* NimBLERemoteService.cpp
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Created: on Jan 27 2020
* Author H2zero
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* Originally:
* http://www.apache.org/licenses/LICENSE-2.0
*
* BLERemoteService.cpp
*
* Created on: Jul 8, 2017
* Author: kolban
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
#include "NimBLERemoteService.h"
#if CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_CENTRAL)
# include "NimBLERemoteService.h"
# include "NimBLERemoteCharacteristic.h"
# include "NimBLEClient.h"
# include "NimBLEAttValue.h"
@@ -144,11 +146,13 @@ int NimBLERemoteService::characteristicDiscCB(uint16_t conn_handle,
const ble_gatt_error* error,
const ble_gatt_chr* chr,
void* arg) {
NIMBLE_LOGD(LOG_TAG, "Characteristic Discovery >>");
NIMBLE_LOGD(LOG_TAG,
"Characteristic Discovery >> status: %d handle: %d",
error->status,
(error->status == 0) ? chr->def_handle : -1);
auto pTaskData = (NimBLETaskData*)arg;
const auto pSvc = (NimBLERemoteService*)pTaskData->m_pInstance;
if (error->status == BLE_HS_ENOTCONN) {
NIMBLE_LOGE(LOG_TAG, "<< Characteristic Discovery; Not connected");
NimBLEUtils::taskRelease(*pTaskData, error->status);
@@ -298,4 +302,4 @@ std::string NimBLERemoteService::toString() const {
return res;
} // toString
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL */
#endif // CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_CENTRAL)

View File

@@ -1,22 +1,25 @@
/*
* NimBLERemoteService.h
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Created: on Jan 27 2020
* Author H2zero
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* Originally:
* http://www.apache.org/licenses/LICENSE-2.0
*
* BLERemoteService.h
*
* Created on: Jul 8, 2017
* Author: kolban
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef NIMBLE_CPP_REMOTE_SERVICE_H_
#define NIMBLE_CPP_REMOTE_SERVICE_H_
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
#include "syscfg/syscfg.h"
#if CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_CENTRAL)
# include "NimBLEAttribute.h"
# include <vector>
@@ -61,5 +64,5 @@ class NimBLERemoteService : public NimBLEAttribute {
uint16_t m_endHandle{0};
}; // NimBLERemoteService
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL */
#endif /* NIMBLE_CPP_REMOTE_SERVICE_H_*/
#endif // CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_CENTRAL)
#endif // NIMBLE_CPP_REMOTE_SERVICE_H_

View File

@@ -1,16 +1,26 @@
/*
* NimBLERemoteValueAttribute.cpp
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Created: on July 28 2024
* Author H2zero
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
#include "NimBLERemoteValueAttribute.h"
#if CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_CENTRAL)
# include "NimBLERemoteValueAttribute.h"
# include "NimBLEClient.h"
# include "NimBLEUtils.h"
# include "NimBLELog.h"
# include <climits>
@@ -111,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.
* @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()");
NimBLEAttValue value{};
@@ -207,4 +217,4 @@ int NimBLERemoteValueAttribute::onReadCB(uint16_t conn_handle, const ble_gatt_er
return rc;
} // onReadCB
#endif // CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL
#endif // CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_CENTRAL)

View File

@@ -1,15 +1,25 @@
/*
* NimBLERemoteValueAttribute.h
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Created: on July 28 2024
* Author H2zero
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef NIMBLE_CPP_REMOTE_VALUE_ATTRIBUTE_H_
#define NIMBLE_CPP_REMOTE_VALUE_ATTRIBUTE_H_
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
#include "syscfg/syscfg.h"
#if CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_CENTRAL)
# if defined(CONFIG_NIMBLE_CPP_IDF)
# include <host/ble_gatt.h>
@@ -22,32 +32,19 @@
# undef max
/**************************/
# include "NimBLEAttribute.h"
# include "NimBLEValueAttribute.h"
# include "NimBLEAttValue.h"
class NimBLEClient;
class NimBLERemoteValueAttribute : public NimBLEAttribute {
class NimBLERemoteValueAttribute : public NimBLEValueAttribute, public NimBLEAttribute {
public:
/**
* @brief Read the value of the remote attribute.
* @param [in] timestamp A pointer to a time_t struct to store the time the value was read.
* @return The value of the remote attribute.
*/
NimBLEAttValue readValue(time_t* timestamp = nullptr) const;
/**
* @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; }
NimBLEAttValue readValue(time_t* timestamp = nullptr);
/**
* Get the client instance that owns this attribute.
@@ -112,13 +109,34 @@ class NimBLERemoteValueAttribute : public NimBLEAttribute {
* @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.
* @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>
# ifdef _DOXYGEN_
bool
# 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
writeValue(const T& v, bool response = false) const {
return writeValue(reinterpret_cast<const uint8_t*>(v.data()), v.size(), response);
@@ -134,7 +152,11 @@ class NimBLERemoteValueAttribute : public NimBLEAttribute {
template <typename T>
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) {
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) {
return writeValue(reinterpret_cast<const uint8_t*>(v.c_str()), v.length(), response);
} else {
@@ -143,20 +165,6 @@ class NimBLERemoteValueAttribute : public NimBLEAttribute {
}
# 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>(&timestamp, 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\>.
* @tparam T The type to convert the data to.
@@ -167,16 +175,16 @@ class NimBLERemoteValueAttribute : public NimBLEAttribute {
* @details <b>Use:</b> <tt>readValue<type>(&timestamp, skipSizeCheck);</tt>
*/
template <typename T>
T readValue(time_t* timestamp = nullptr, bool skipSizeCheck = false) const {
T readValue(time_t* timestamp = nullptr, bool skipSizeCheck = false) {
readValue();
return m_value.getValue<T>(timestamp, skipSizeCheck);
return getValue<T>(timestamp, skipSizeCheck);
}
protected:
/**
* @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.
@@ -185,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 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_

View File

@@ -1,21 +1,23 @@
/*
* NimBLEScan.cpp
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Created: on Jan 24 2020
* Author H2zero
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* Originally:
* http://www.apache.org/licenses/LICENSE-2.0
*
* BLEScan.cpp
*
* Created on: Jul 1, 2017
* Author: kolban
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
#include "NimBLEScan.h"
#if CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_OBSERVER)
# include "NimBLEScan.h"
# include "NimBLEDevice.h"
# include "NimBLELog.h"
@@ -39,7 +41,9 @@ NimBLEScan::NimBLEScan()
* @brief Scan destructor, release any allocated resources.
*/
NimBLEScan::~NimBLEScan() {
clearResults();
for (const auto& dev : m_scanResults.m_deviceVec) {
delete dev;
}
}
/**
@@ -54,7 +58,12 @@ int NimBLEScan::handleGapEvent(ble_gap_event* event, void* arg) {
switch (event->type) {
case BLE_GAP_EVENT_EXT_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 bool isLegacyAdv = disc.props & BLE_HCI_ADV_LEGACY_MASK;
const auto event_type = isLegacyAdv ? disc.legacy_event_type : disc.props;
@@ -65,7 +74,7 @@ int NimBLEScan::handleGapEvent(ble_gap_event* event, void* arg) {
# endif
NimBLEAddress advertisedAddress(disc.addr);
# ifdef CONFIG_BT_NIMBLE_ROLE_CENTRAL
# if MYNEWT_VAL(BLE_ROLE_CENTRAL)
// stop processing if already connected
NimBLEClient* pClient = NimBLEDevice::getClientByPeerAddress(advertisedAddress);
if (pClient != nullptr && pClient->isConnected()) {
@@ -77,7 +86,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
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.
if (dev->getAddress() == advertisedAddress && dev->getSetId() == disc.sid)
# else
@@ -108,27 +117,36 @@ int NimBLEScan::handleGapEvent(ble_gap_event* event, void* arg) {
NIMBLE_LOGI(LOG_TAG, "New advertiser: %s", advertisedAddress.toString().c_str());
} else {
advertisedDevice->update(event, event_type);
if (isLegacyAdv && event_type == BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP) {
NIMBLE_LOGI(LOG_TAG, "Scan response from: %s", advertisedAddress.toString().c_str());
} else {
NIMBLE_LOGI(LOG_TAG, "Duplicate; updated: %s", advertisedAddress.toString().c_str());
if (isLegacyAdv) {
if (event_type == BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP) {
NIMBLE_LOGI(LOG_TAG, "Scan response from: %s", advertisedAddress.toString().c_str());
} else {
NIMBLE_LOGI(LOG_TAG, "Duplicate; updated: %s", advertisedAddress.toString().c_str());
}
}
}
# 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) {
pScan->m_pScanCallbacks->onDiscovered(advertisedDevice);
advertisedDevice->m_callbackSent++;
pScan->m_pScanCallbacks->onDiscovered(advertisedDevice);
}
// If not active scanning or scan response is not available
// or extended advertisement scanning, report the result to the callback now.
if (pScan->m_scanParams.passive || !isLegacyAdv || !advertisedDevice->isScannable()) {
pScan->m_pScanCallbacks->onResult(advertisedDevice);
advertisedDevice->m_callbackSent++;
pScan->m_pScanCallbacks->onResult(advertisedDevice);
} else if (isLegacyAdv && event_type == BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP) {
advertisedDevice->m_callbackSent++;
// got the scan response report the full data.
pScan->m_pScanCallbacks->onResult(advertisedDevice);
advertisedDevice->m_callbackSent++;
}
// If not storing results and we have invoked the callback, delete the device.
@@ -263,7 +281,7 @@ bool NimBLEScan::isScanning() {
return ble_gap_disc_active();
}
# if CONFIG_BT_NIMBLE_EXT_ADV
# if MYNEWT_VAL(BLE_EXT_ADV)
/**
* @brief Set the PHYs to scan.
* @param [in] phyMask The PHYs to scan, a bit mask of:
@@ -315,7 +333,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 CONFIG_BT_NIMBLE_EXT_ADV
# if MYNEWT_VAL(BLE_EXT_ADV)
ble_gap_ext_disc_params scan_params;
scan_params.passive = m_scanParams.passive;
scan_params.itvl = m_scanParams.itvl;
@@ -465,20 +483,26 @@ NimBLEScanResults NimBLEScan::getResults() {
* @brief Clear the stored results of the scan.
*/
void NimBLEScan::clearResults() {
for (const auto& dev : m_scanResults.m_deviceVec) {
delete dev;
if (m_scanResults.m_deviceVec.size()) {
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
/**
* @brief Dump the scan results to the log.
*/
void NimBLEScanResults::dump() const {
# if MYNEWT_VAL(NIMBLE_CPP_LOG_LEVEL) >= 3
for (const auto& dev : m_deviceVec) {
(void)dev; // suppress unused variable warning when log level is less than info
NIMBLE_LOGI(LOG_TAG, "- %s", dev->toString().c_str());
}
# endif
} // dump
/**
@@ -545,4 +569,4 @@ void NimBLEScanCallbacks::onScanEnd(const NimBLEScanResults& results, int reason
NIMBLE_LOGD(CB_TAG, "Scan ended; reason %d, num results: %d", reason, results.getCount());
}
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_OBSERVER */
#endif // CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_OBSERVER)

View File

@@ -1,21 +1,25 @@
/*
* NimBLEScan.h
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Created: on Jan 24 2020
* Author H2zero
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* Originally:
* http://www.apache.org/licenses/LICENSE-2.0
*
* BLEScan.h
*
* Created on: Jul 1, 2017
* Author: kolban
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef NIMBLE_CPP_SCAN_H_
#define NIMBLE_CPP_SCAN_H_
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
#include "syscfg/syscfg.h"
#if CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_OBSERVER)
# include "NimBLEAdvertisedDevice.h"
# include "NimBLEUtils.h"
@@ -79,7 +83,7 @@ class NimBLEScan {
void erase(const NimBLEAddress& address);
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 };
void setPhy(Phy phyMask);
void setPeriod(uint32_t periodMs);
@@ -99,7 +103,7 @@ class NimBLEScan {
NimBLETaskData* m_pTaskData;
uint8_t m_maxResults;
# if CONFIG_BT_NIMBLE_EXT_ADV
# if MYNEWT_VAL(BLE_EXT_ADV)
uint8_t m_phy{SCAN_ALL};
uint16_t m_period{0};
# endif
@@ -132,5 +136,5 @@ class NimBLEScanCallbacks {
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_

View File

@@ -1,25 +1,27 @@
/*
* NimBLEServer.cpp
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Created: on March 2, 2020
* Author H2zero
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* Originally:
* http://www.apache.org/licenses/LICENSE-2.0
*
* BLEServer.cpp
*
* Created on: Apr 16, 2017
* Author: kolban
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#include "NimBLEServer.h"
#if CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_PERIPHERAL)
# include "NimBLEServer.h"
# include "NimBLEDevice.h"
# include "NimBLELog.h"
# if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
# if MYNEWT_VAL(BLE_ROLE_CENTRAL)
# include "NimBLEClient.h"
# endif
@@ -47,7 +49,7 @@ NimBLEServer::NimBLEServer()
: m_gattsStarted{false},
m_svcChanged{false},
m_deleteCallbacks{false},
# if !CONFIG_BT_NIMBLE_EXT_ADV
# if !MYNEWT_VAL(BLE_EXT_ADV)
m_advertiseOnDisconnect{false},
# endif
m_pServerCallbacks{&defaultCallbacks},
@@ -67,9 +69,11 @@ NimBLEServer::~NimBLEServer() {
delete m_pServerCallbacks;
}
# if MYNEWT_VAL(BLE_ROLE_CENTRAL)
if (m_pClient != nullptr) {
delete m_pClient;
}
# endif
}
/**
@@ -139,17 +143,32 @@ NimBLEService* NimBLEServer::getServiceByHandle(uint16_t handle) const {
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.
* @return A pinter to an advertising object.
* @return A pointer to an advertising object.
*/
NimBLEExtAdvertising* NimBLEServer::getAdvertising() const {
return NimBLEDevice::getAdvertising();
} // getAdvertising
# 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.
* @return A pointer to an advertising object.
@@ -185,7 +204,7 @@ void NimBLEServer::start() {
return;
}
# if CONFIG_NIMBLE_CPP_LOG_LEVEL >= 4
# if MYNEWT_VAL(NIMBLE_CPP_LOG_LEVEL) >= 4
ble_gatts_show_local();
# endif
@@ -246,7 +265,7 @@ bool NimBLEServer::disconnect(const NimBLEConnInfo& connInfo, uint8_t reason) co
return disconnect(connInfo.getConnHandle(), reason);
} // 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.
* @param [in] enable true == advertise, false == don't advertise.
@@ -301,7 +320,7 @@ NimBLEConnInfo NimBLEServer::getPeerInfo(uint8_t index) const {
for (const auto& peer : m_connectedPeers) {
if (peer != BLE_HS_CONN_HANDLE_NONE) {
if (count == index) {
return getPeerInfoByHandle(m_connectedPeers[count]);
return getPeerInfoByHandle(peer);
}
count++;
}
@@ -350,9 +369,14 @@ int NimBLEServer::handleGapEvent(ble_gap_event* event, void* arg) {
switch (event->type) {
case BLE_GAP_EVENT_CONNECT: {
if (event->connect.status != 0) {
NIMBLE_LOGE(LOG_TAG, "Connection failed");
# if !CONFIG_BT_NIMBLE_EXT_ADV
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) {
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();
# endif
} else {
@@ -396,10 +420,12 @@ 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 this was also the client make sure it's flagged as disconnected.
pServer->m_pClient->m_connHandle = BLE_HS_CONN_HANDLE_NONE;
}
# endif
if (pServer->m_svcChanged) {
pServer->resetGATT();
@@ -407,7 +433,7 @@ int NimBLEServer::handleGapEvent(ble_gap_event* event, void* arg) {
peerInfo.m_desc = event->disconnect.conn;
pServer->m_pServerCallbacks->onDisconnect(pServer, peerInfo, event->disconnect.reason);
# if !CONFIG_BT_NIMBLE_EXT_ADV
# if !MYNEWT_VAL(BLE_EXT_ADV)
if (pServer->m_advertiseOnDisconnect) {
pServer->startAdvertising();
}
@@ -416,33 +442,21 @@ int NimBLEServer::handleGapEvent(ble_gap_event* event, void* arg) {
} // BLE_GAP_EVENT_DISCONNECT
case BLE_GAP_EVENT_SUBSCRIBE: {
NIMBLE_LOGI(LOG_TAG,
"subscribe event; attr_handle=%d, subscribed: %s",
event->subscribe.attr_handle,
((event->subscribe.cur_notify || 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);
}
}
rc = ble_gap_conn_find(event->subscribe.conn_handle, &peerInfo.m_desc);
if (rc != 0) {
break;
}
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;
} // BLE_GAP_EVENT_SUBSCRIBE
@@ -456,18 +470,14 @@ int NimBLEServer::handleGapEvent(ble_gap_event* event, void* arg) {
} // BLE_GAP_EVENT_MTU
case BLE_GAP_EVENT_NOTIFY_TX: {
NimBLECharacteristic* pChar = nullptr;
for (const auto& svc : pServer->m_svcVec) {
for (auto& chr : svc->m_vChars) {
if (chr->getHandle() == event->notify_tx.attr_handle) {
pChar = chr;
}
}
rc = ble_gap_conn_find(event->notify_tx.conn_handle, &peerInfo.m_desc);
if (rc != 0) {
break;
}
auto pChar = pServer->getCharacteristicByHandle(event->notify_tx.attr_handle);
if (pChar == nullptr) {
return 0;
break;
}
if (event->notify_tx.indication) {
@@ -477,14 +487,24 @@ int NimBLEServer::handleGapEvent(ble_gap_event* event, void* arg) {
}
pChar->m_pCallbacks->onStatus(pChar, event->notify_tx.status);
pChar->m_pCallbacks->onStatus(pChar, peerInfo, event->notify_tx.status);
break;
} // 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: {
# if CONFIG_BT_NIMBLE_EXT_ADV
# if MYNEWT_VAL(BLE_EXT_ADV) && MYNEWT_VAL(BLE_ROLE_BROADCASTER)
case BLE_GAP_EVENT_SCAN_REQ_RCVD:
return NimBLEExtAdvertising::handleGapEvent(event, arg);
# else
# elif MYNEWT_VAL(BLE_ROLE_BROADCASTER)
return NimBLEAdvertising::handleGapEvent(event, arg);
# endif
} // BLE_GAP_EVENT_ADV_COMPLETE | BLE_GAP_EVENT_SCAN_REQ_RCVD
@@ -524,6 +544,18 @@ int NimBLEServer::handleGapEvent(ble_gap_event* event, void* arg) {
}
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;
} // BLE_GAP_EVENT_ENC_CHANGE
@@ -537,7 +569,6 @@ int NimBLEServer::handleGapEvent(ble_gap_event* event, void* arg) {
break;
} // BLE_GAP_EVENT_IDENTITY_RESOLVED
# if CONFIG_BT_NIMBLE_EXT_ADV
case BLE_GAP_EVENT_PHY_UPDATE_COMPLETE: {
rc = ble_gap_conn_find(event->phy_updated.conn_handle, &peerInfo.m_desc);
if (rc != 0) {
@@ -547,7 +578,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);
return 0;
} // BLE_GAP_EVENT_PHY_UPDATE_COMPLETE
# endif
case BLE_GAP_EVENT_PASSKEY_ACTION: {
struct ble_sm_io pkey = {0, 0};
@@ -604,21 +634,19 @@ int NimBLEServer::handleGattEvent(uint16_t connHandle, uint16_t attrHandle, ble_
NIMBLE_LOGD(LOG_TAG,
"Gatt %s event",
(ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR || ctxt->op == BLE_GATT_ACCESS_OP_READ_DSC) ? "Read" : "Write");
auto pAtt = static_cast<NimBLELocalValueAttribute*>(arg);
auto val = pAtt->getAttVal();
auto pAtt = static_cast<NimBLELocalValueAttribute*>(arg);
const NimBLEAttValue& val = pAtt->getAttVal();
NimBLEConnInfo peerInfo{};
ble_gap_conn_find(connHandle, &peerInfo.m_desc);
switch (ctxt->op) {
case BLE_GATT_ACCESS_OP_READ_DSC:
case BLE_GATT_ACCESS_OP_READ_CHR: {
// Don't call readEvent if this is an internal read (handle is NONE)
if (connHandle != BLE_HS_CONN_HANDLE_NONE) {
// If the packet header is only 8 bytes then this is a follow up of a long read
// so we don't want to call the onRead() callback again.
if (ctxt->om->om_pkthdr_len > 8 || pAtt->getAttVal().size() <= (ble_att_mtu(connHandle) - 3)) {
pAtt->readEvent(peerInfo);
}
// Don't call readEvent if the buffer len is 0 (this is a follow up to a previous read),
// or if this is an internal read (handle is NONE)
if (ctxt->om->om_len > 0 && connHandle != BLE_HS_CONN_HANDLE_NONE) {
pAtt->readEvent(peerInfo);
}
ble_npl_hw_enter_critical();
@@ -630,12 +658,12 @@ int NimBLEServer::handleGattEvent(uint16_t connHandle, uint16_t attrHandle, ble_
case BLE_GATT_ACCESS_OP_WRITE_DSC:
case BLE_GATT_ACCESS_OP_WRITE_CHR: {
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;
}
uint8_t buf[maxLen];
uint16_t len = ctxt->om->om_len;
uint8_t buf[maxLen];
memcpy(buf, ctxt->om->om_data, len);
os_mbuf* next;
@@ -723,7 +751,7 @@ void NimBLEServer::removeService(NimBLEService* service, bool deleteSvc) {
service->setRemoved(deleteSvc ? NIMBLE_ATT_REMOVE_DELETE : NIMBLE_ATT_REMOVE_HIDE);
serviceChanged();
# if !CONFIG_BT_NIMBLE_EXT_ADV
# if !MYNEWT_VAL(BLE_EXT_ADV) && MYNEWT_VAL(BLE_ROLE_BROADCASTER)
NimBLEDevice::getAdvertising()->removeServiceUUID(service->getUUID());
# endif
} // removeService
@@ -760,7 +788,9 @@ void NimBLEServer::resetGATT() {
return;
}
# if MYNEWT_VAL(BLE_ROLE_BROADCASTER)
NimBLEDevice::stopAdvertising();
# endif
ble_gatts_reset();
ble_svc_gap_init();
ble_svc_gatt_init();
@@ -783,29 +813,6 @@ void NimBLEServer::resetGATT() {
m_gattsStarted = false;
} // 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.
* @param [in] connHandle the connection handle to the update the PHY for.
@@ -849,9 +856,33 @@ bool NimBLEServer::getPhy(uint16_t connHandle, uint8_t* txPhy, uint8_t* rxPhy) {
return rc == 0;
} // 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
# if !CONFIG_BT_NIMBLE_EXT_ADV || defined(_DOXYGEN_)
# if (!MYNEWT_VAL(BLE_EXT_ADV) && MYNEWT_VAL(BLE_ROLE_BROADCASTER)) || defined(_DOXYGEN_)
/**
* @brief Start advertising.
* @param [in] duration The duration in milliseconds to advertise for, default = forever.
@@ -928,7 +959,7 @@ void NimBLEServer::setDataLen(uint16_t connHandle, uint16_t octets) const {
# endif
} // setDataLen
# if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
# if MYNEWT_VAL(BLE_ROLE_CENTRAL)
/**
* @brief Create a client instance from the connection handle.
* @param [in] connHandle The connection handle to create a client instance from.
@@ -1011,10 +1042,8 @@ void NimBLEServerCallbacks::onConnParamsUpdate(NimBLEConnInfo& connInfo) {
NIMBLE_LOGD("NimBLEServerCallbacks", "onConnParamsUpdate: default");
} // onConnParamsUpdate
# if CONFIG_BT_NIMBLE_EXT_ADV
void NimBLEServerCallbacks::onPhyUpdate(NimBLEConnInfo& connInfo, uint8_t txPhy, uint8_t rxPhy) {
NIMBLE_LOGD("NimBLEServerCallbacks", "onPhyUpdate: default, txPhy: %d, rxPhy: %d", txPhy, rxPhy);
} // onPhyUpdate
# endif
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL */
#endif // CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_PERIPHERAL)

View File

@@ -1,22 +1,25 @@
/*
* NimBLEServer.h
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Created: on March 2, 2020
* Author H2zero
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* Originally:
* http://www.apache.org/licenses/LICENSE-2.0
*
* BLEServer.h
*
* Created on: Apr 16, 2017
* Author: kolban
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef NIMBLE_CPP_SERVER_H_
#define NIMBLE_CPP_SERVER_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)
# if defined(CONFIG_NIMBLE_CPP_IDF)
# include "host/ble_gap.h"
@@ -42,12 +45,14 @@ class NimBLEConnInfo;
class NimBLEAddress;
class NimBLEService;
class NimBLECharacteristic;
# if CONFIG_BT_NIMBLE_EXT_ADV
# if MYNEWT_VAL(BLE_ROLE_BROADCASTER)
# if MYNEWT_VAL(BLE_EXT_ADV)
class NimBLEExtAdvertising;
# else
# else
class NimBLEAdvertising;
# endif
# endif
# if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
# if MYNEWT_VAL(BLE_ROLE_CENTRAL)
class NimBLEClient;
# endif
@@ -67,6 +72,7 @@ class NimBLEServer {
NimBLEService* getServiceByUUID(const char* uuid, uint16_t instanceId = 0) const;
NimBLEService* getServiceByUUID(const NimBLEUUID& uuid, uint16_t instanceId = 0) const;
NimBLEService* getServiceByHandle(uint16_t handle) const;
NimBLECharacteristic* getCharacteristicByHandle(uint16_t handle) const;
void removeService(NimBLEService* service, bool deleteSvc = false);
void addService(NimBLEService* service);
uint16_t getPeerMTU(uint16_t connHandle) const;
@@ -76,59 +82,61 @@ class NimBLEServer {
NimBLEConnInfo getPeerInfoByHandle(uint16_t connHandle) const;
void advertiseOnDisconnect(bool enable);
void setDataLen(uint16_t connHandle, uint16_t tx_octets) const;
bool updatePhy(uint16_t connHandle, uint8_t txPhysMask, uint8_t rxPhysMask, uint16_t phyOptions);
bool getPhy(uint16_t connHandle, uint8_t* txPhy, uint8_t* rxPhy);
# if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
# if MYNEWT_VAL(BLE_ROLE_CENTRAL)
NimBLEClient* getClient(uint16_t connHandle);
NimBLEClient* getClient(const NimBLEConnInfo& connInfo);
void deleteClient();
# endif
# if CONFIG_BT_NIMBLE_EXT_ADV
# if MYNEWT_VAL(BLE_ROLE_BROADCASTER)
# if MYNEWT_VAL(BLE_EXT_ADV)
NimBLEExtAdvertising* getAdvertising() const;
bool startAdvertising(uint8_t instanceId, int duration = 0, int maxEvents = 0) const;
bool stopAdvertising(uint8_t instanceId) 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);
# endif
# endif
# if !CONFIG_BT_NIMBLE_EXT_ADV || defined(_DOXYGEN_)
# if !MYNEWT_VAL(BLE_EXT_ADV) || defined(_DOXYGEN_)
NimBLEAdvertising* getAdvertising() const;
bool startAdvertising(uint32_t duration = 0) const;
bool stopAdvertising() const;
# endif
# endif
private:
friend class NimBLEDevice;
friend class NimBLEService;
friend class NimBLECharacteristic;
# if CONFIG_BT_NIMBLE_EXT_ADV
# if MYNEWT_VAL(BLE_ROLE_BROADCASTER)
# if MYNEWT_VAL(BLE_EXT_ADV)
friend class NimBLEExtAdvertising;
# else
# else
friend class NimBLEAdvertising;
# endif
# endif
NimBLEServer();
~NimBLEServer();
bool m_gattsStarted : 1;
bool m_svcChanged : 1;
bool m_deleteCallbacks : 1;
# if !CONFIG_BT_NIMBLE_EXT_ADV
bool m_advertiseOnDisconnect : 1;
# endif
NimBLEServerCallbacks* m_pServerCallbacks;
std::vector<NimBLEService*> m_svcVec;
std::array<uint16_t, CONFIG_BT_NIMBLE_MAX_CONNECTIONS> m_connectedPeers;
# if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
NimBLEClient* m_pClient{nullptr};
# 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();
bool m_gattsStarted : 1;
bool m_svcChanged : 1;
bool m_deleteCallbacks : 1;
# if !MYNEWT_VAL(BLE_EXT_ADV)
bool m_advertiseOnDisconnect : 1;
# endif
NimBLEServerCallbacks* m_pServerCallbacks;
std::vector<NimBLEService*> m_svcVec;
std::array<uint16_t, MYNEWT_VAL(BLE_MAX_CONNECTIONS)> m_connectedPeers;
# if MYNEWT_VAL(BLE_ROLE_CENTRAL)
NimBLEClient* m_pClient{nullptr};
# endif
}; // NimBLEServer
/**
@@ -200,7 +208,6 @@ class NimBLEServerCallbacks {
*/
virtual void onConnParamsUpdate(NimBLEConnInfo& connInfo);
# if CONFIG_BT_NIMBLE_EXT_ADV
/**
* @brief Called when the PHY update procedure is complete.
* @param [in] connInfo A reference to a NimBLEConnInfo instance with information
@@ -213,8 +220,7 @@ class NimBLEServerCallbacks {
* * BLE_GAP_LE_PHY_CODED
*/
virtual void onPhyUpdate(NimBLEConnInfo& connInfo, uint8_t txPhy, uint8_t rxPhy);
# endif
}; // 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_

View File

@@ -1,24 +1,24 @@
/*
* NimBLEService.cpp
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Created: on March 2, 2020
* Author H2zero
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* Originally:
* http://www.apache.org/licenses/LICENSE-2.0
*
* BLEService.cpp
*
* Created on: Mar 25, 2017
* Author: kolban
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// A service is identified by a UUID. A service is also the container for one or more characteristics.
#include "NimBLEService.h"
#if CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_PERIPHERAL)
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
# include "NimBLEService.h"
# if CONFIG_BT_NIMBLE_EXT_ADV
# if MYNEWT_VAL(BLE_EXT_ADV)
# include "NimBLEExtAdvertising.h"
# else
# include "NimBLEAdvertising.h"
@@ -90,6 +90,13 @@ void NimBLEService::dump() const {
*/
bool NimBLEService::start() {
NIMBLE_LOGD(LOG_TAG, ">> start(): Starting service: %s", toString().c_str());
// If started previously and no characteristics have been added or removed,
// then we can skip the service registration process.
if (m_pSvcDef->characteristics && !getServer()->m_svcChanged) {
return true;
}
// If started previously, clear everything and start over
if (m_pSvcDef->characteristics) {
if (m_pSvcDef->characteristics->descriptors) {
@@ -368,4 +375,4 @@ bool NimBLEService::isStarted() const {
return m_pSvcDef->type > 0;
} // isStarted
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL */
#endif // CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_PERIPHERAL)

View File

@@ -1,22 +1,25 @@
/*
* NimBLEService.h
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Created: on March 2, 2020
* Author H2zero
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* Originally:
* http://www.apache.org/licenses/LICENSE-2.0
*
* BLEService.h
*
* Created on: Mar 25, 2017
* Author: kolban
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef NIMBLE_CPP_SERVICE_H_
#define NIMBLE_CPP_SERVICE_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 NimBLEService;
@@ -66,5 +69,5 @@ class NimBLEService : public NimBLELocalAttribute {
ble_gatt_svc_def m_pSvcDef[2]{};
}; // NimBLEService
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL */
#endif /* NIMBLE_CPP_SERVICE_H_ */
#endif // CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_PERIPHERAL)
#endif // NIMBLE_CPP_SERVICE_H_

498
src/NimBLEStream.cpp Normal file
View File

@@ -0,0 +1,498 @@
/*
* 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.
*/
#ifdef ESP_PLATFORM
# include "NimBLEStream.h"
# if CONFIG_BT_NIMBLE_ENABLED && (MYNEWT_VAL(BLE_ROLE_PERIPHERAL) || MYNEWT_VAL(BLE_ROLE_CENTRAL))
# include "NimBLEDevice.h"
# include "rom/uart.h"
static const char* LOG_TAG = "NimBLEStream";
// Stub Print/Stream implementations when Arduino not available
# if !NIMBLE_CPP_ARDUINO_STRING_AVAILABLE
size_t Print::print(const char* s) {
if (!s) return 0;
return write(reinterpret_cast<const uint8_t*>(s), strlen(s));
}
size_t Print::println(const char* s) {
size_t n = print(s);
static const char crlf[] = "\r\n";
n += write(reinterpret_cast<const uint8_t*>(crlf), 2);
return n;
}
size_t Print::printf(const char* fmt, ...) {
if (!fmt) {
return 0;
}
char stackBuf[128];
va_list ap;
va_start(ap, fmt);
int n = vsnprintf(stackBuf, sizeof(stackBuf), fmt, ap);
va_end(ap);
if (n < 0) {
return 0;
}
if (static_cast<size_t>(n) < sizeof(stackBuf)) {
return write(reinterpret_cast<const uint8_t*>(stackBuf), static_cast<size_t>(n));
}
// allocate for larger output
size_t needed = static_cast<size_t>(n) + 1;
char* buf = static_cast<char*>(malloc(needed));
if (!buf) {
return 0;
}
va_start(ap, fmt);
vsnprintf(buf, needed, fmt, ap);
va_end(ap);
size_t ret = write(reinterpret_cast<const uint8_t*>(buf), static_cast<size_t>(n));
free(buf);
return ret;
}
# endif
void NimBLEStream::txTask(void* arg) {
NimBLEStream* pStream = static_cast<NimBLEStream*>(arg);
for (;;) {
size_t itemSize = 0;
void* item = xRingbufferReceive(pStream->m_txBuf, &itemSize, portMAX_DELAY);
if (item) {
pStream->send(reinterpret_cast<uint8_t*>(item), itemSize);
vRingbufferReturnItem(pStream->m_txBuf, item);
}
}
}
bool NimBLEStream::begin() {
if (m_txBuf || m_rxBuf || m_txTask) {
NIMBLE_UART_LOGW(LOG_TAG, "Already initialized");
return true;
}
if (m_txBufSize) {
m_txBuf = xRingbufferCreate(m_txBufSize, RINGBUF_TYPE_BYTEBUF);
if (!m_txBuf) {
NIMBLE_UART_LOGE(LOG_TAG, "Failed to create TX ringbuffer");
return false;
}
}
if (m_rxBufSize) {
m_rxBuf = xRingbufferCreate(m_rxBufSize, RINGBUF_TYPE_BYTEBUF);
if (!m_rxBuf) {
NIMBLE_UART_LOGE(LOG_TAG, "Failed to create RX ringbuffer");
if (m_txBuf) {
vRingbufferDelete(m_txBuf);
m_txBuf = nullptr;
}
return false;
}
}
if (xTaskCreate(txTask, "NimBLEStreamTx", m_txTaskStackSize, this, m_txTaskPriority, &m_txTask) != pdPASS) {
NIMBLE_UART_LOGE(LOG_TAG, "Failed to create stream tx task");
if (m_rxBuf) {
vRingbufferDelete(m_rxBuf);
m_rxBuf = nullptr;
}
if (m_txBuf) {
vRingbufferDelete(m_txBuf);
m_txBuf = nullptr;
}
return false;
}
return true;
}
bool NimBLEStream::end() {
if (m_txTask) {
vTaskDelete(m_txTask);
m_txTask = nullptr;
}
if (m_txBuf) {
vRingbufferDelete(m_txBuf);
m_txBuf = nullptr;
}
if (m_rxBuf) {
vRingbufferDelete(m_rxBuf);
m_rxBuf = nullptr;
}
m_hasPeek = false;
return true;
}
size_t NimBLEStream::write(const uint8_t* data, size_t len) {
if (!m_txBuf || !data || len == 0) {
return 0;
}
ble_npl_time_t timeout = 0;
ble_npl_time_ms_to_ticks(getTimeout(), &timeout);
size_t chunk = std::min(len, xRingbufferGetCurFreeSize(m_txBuf));
if (xRingbufferSend(m_txBuf, data, chunk, static_cast<TickType_t>(timeout)) != pdTRUE) {
return 0;
}
return chunk;
}
size_t NimBLEStream::availableForWrite() const {
return m_txBuf ? xRingbufferGetCurFreeSize(m_txBuf) : 0;
}
void NimBLEStream::flush() {
// Wait until TX ring is drained
while (m_txBuf && xRingbufferGetCurFreeSize(m_txBuf) < m_txBufSize) {
ble_npl_time_delay(ble_npl_time_ms_to_ticks32(1));
}
}
int NimBLEStream::available() {
if (!m_rxBuf) {
NIMBLE_UART_LOGE(LOG_TAG, "Invalid RX buffer");
return 0;
}
if (m_hasPeek) {
return 1; // at least the peeked byte
}
// Query items in RX ring
UBaseType_t waiting = 0;
vRingbufferGetInfo(m_rxBuf, nullptr, nullptr, nullptr, nullptr, &waiting);
return static_cast<int>(waiting);
}
int NimBLEStream::read() {
if (!m_rxBuf) {
return -1;
}
// Return peeked byte if available
if (m_hasPeek) {
m_hasPeek = false;
return static_cast<int>(m_peekByte);
}
size_t itemSize = 0;
uint8_t* item = static_cast<uint8_t*>(xRingbufferReceive(m_rxBuf, &itemSize, 0));
if (!item || itemSize == 0) return -1;
uint8_t byte = item[0];
// If item has more bytes, put the rest back
if (itemSize > 1) {
xRingbufferSend(m_rxBuf, item + 1, itemSize - 1, 0);
}
vRingbufferReturnItem(m_rxBuf, item);
return static_cast<int>(byte);
}
int NimBLEStream::peek() {
if (!m_rxBuf) {
return -1;
}
if (m_hasPeek) {
return static_cast<int>(m_peekByte);
}
size_t itemSize = 0;
uint8_t* item = static_cast<uint8_t*>(xRingbufferReceive(m_rxBuf, &itemSize, 0));
if (!item || itemSize == 0) {
return -1;
}
m_peekByte = item[0];
m_hasPeek = true;
// Put the entire item back
xRingbufferSend(m_rxBuf, item, itemSize, 0);
vRingbufferReturnItem(m_rxBuf, item);
return static_cast<int>(m_peekByte);
}
size_t NimBLEStream::pushRx(const uint8_t* data, size_t len) {
if (!m_rxBuf || !data || len == 0) {
NIMBLE_UART_LOGE(LOG_TAG, "Invalid RX buffer or data");
return 0;
}
// Clear peek state when new data arrives
m_hasPeek = false;
if (xRingbufferSend(m_rxBuf, data, len, 0) != pdTRUE) {
NIMBLE_UART_LOGE(LOG_TAG, "RX buffer full, dropping %u bytes", len);
return 0;
}
return len;
}
# if MYNEWT_VAL(BLE_ROLE_PERIPHERAL)
bool NimBLEStreamServer::init(const NimBLEUUID& svcUuid, const NimBLEUUID& chrUuid, bool canWrite, bool secure) {
if (!NimBLEDevice::isInitialized()) {
NIMBLE_UART_LOGE(LOG_TAG, "NimBLEDevice not initialized");
return false;
}
NimBLEServer* pServer = NimBLEDevice::getServer();
if (!pServer) {
pServer = NimBLEDevice::createServer();
}
NimBLEService* pSvc = pServer->getServiceByUUID(svcUuid);
if (!pSvc) {
pSvc = pServer->createService(svcUuid);
}
if (!pSvc) {
NIMBLE_UART_LOGE(LOG_TAG, "Failed to create service");
return false;
}
// Create characteristic with notify + write properties for bidirectional stream
uint32_t props = NIMBLE_PROPERTY::NOTIFY;
if (secure) {
props |= NIMBLE_PROPERTY::READ_ENC;
}
if (canWrite) {
props |= NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::WRITE_NR;
if (secure) {
props |= NIMBLE_PROPERTY::WRITE_ENC;
}
} else {
m_rxBufSize = 0; // disable RX if not writable
}
m_pChr = pSvc->getCharacteristic(chrUuid);
if (!m_pChr) {
m_pChr = pSvc->createCharacteristic(chrUuid, props);
}
if (!m_pChr) {
NIMBLE_UART_LOGE(LOG_TAG, "Failed to create characteristic");
return false;
}
m_pChr->setCallbacks(&m_charCallbacks);
return pSvc->start();
}
void NimBLEStreamServer::deinit() {
if (m_pChr) {
NimBLEService* pSvc = m_pChr->getService();
if (pSvc) {
pSvc->removeCharacteristic(m_pChr, true);
}
m_pChr = nullptr;
}
NimBLEStream::end();
}
size_t NimBLEStreamServer::write(const uint8_t* data, size_t len) {
if (!m_pChr || len == 0 || !hasSubscriber()) {
return 0;
}
# if MYNEWT_VAL(NIMBLE_CPP_LOG_LEVEL) >= 4
// Skip server gap events to avoid log recursion
static const char filterStr[] = "handleGapEvent";
constexpr size_t filterLen = sizeof(filterStr) - 1;
if (len >= filterLen + 3) {
for (size_t i = 3; i <= len - filterLen; i++) {
if (memcmp(data + i, filterStr, filterLen) == 0) {
return len; // drop to avoid recursion
}
}
}
# endif
return NimBLEStream::write(data, len);
}
bool NimBLEStreamServer::send(const uint8_t* data, size_t len) {
if (!m_pChr || !len || !hasSubscriber()) {
return false;
}
size_t offset = 0;
while (offset < len) {
size_t chunkLen = std::min(len - offset, getMaxLength());
while (!m_pChr->notify(data + offset, chunkLen, getPeerHandle())) {
// Retry on ENOMEM (mbuf shortage)
if (m_rc == BLE_HS_ENOMEM || os_msys_num_free() <= 2) {
ble_npl_time_delay(ble_npl_time_ms_to_ticks32(8)); // wait for a minimum connection event time
continue;
}
return false;
}
offset += chunkLen;
}
return true;
}
void NimBLEStreamServer::ChrCallbacks::onWrite(NimBLECharacteristic* pCharacteristic, NimBLEConnInfo& connInfo) {
// Push received data into RX buffer
auto val = pCharacteristic->getValue();
if (val.size() > 0) {
m_parent->pushRx(val.data(), val.size());
}
if (m_userCallbacks) {
m_userCallbacks->onWrite(pCharacteristic, connInfo);
}
}
void NimBLEStreamServer::ChrCallbacks::onSubscribe(NimBLECharacteristic* pCharacteristic,
NimBLEConnInfo& connInfo,
uint16_t subValue) {
// only one subscriber supported
if (m_peerHandle != BLE_HS_CONN_HANDLE_NONE && subValue) {
return;
}
m_peerHandle = subValue ? connInfo.getConnHandle() : BLE_HS_CONN_HANDLE_NONE;
if (m_peerHandle != BLE_HS_CONN_HANDLE_NONE) {
m_maxLen = ble_att_mtu(m_peerHandle) - 3;
if (!m_parent->begin()) {
NIMBLE_UART_LOGE(LOG_TAG, "NimBLEStreamServer failed to begin");
}
return;
}
m_parent->end();
if (m_userCallbacks) {
m_userCallbacks->onSubscribe(pCharacteristic, connInfo, subValue);
}
}
void NimBLEStreamServer::ChrCallbacks::onStatus(NimBLECharacteristic* pCharacteristic, int code) {
m_parent->m_rc = code;
if (m_userCallbacks) {
m_userCallbacks->onStatus(pCharacteristic, code);
}
}
# endif // MYNEWT_VAL(BLE_ROLE_PERIPHERAL)
# if MYNEWT_VAL(BLE_ROLE_CENTRAL)
bool NimBLEStreamClient::init(NimBLERemoteCharacteristic* pChr, bool subscribe) {
if (!pChr) {
return false;
}
m_pChr = pChr;
m_writeWithRsp = !pChr->canWriteNoResponse();
// Subscribe to notifications/indications for RX if requested
if (subscribe && (pChr->canNotify() || pChr->canIndicate())) {
using namespace std::placeholders;
if (!pChr->subscribe(pChr->canNotify(), std::bind(&NimBLEStreamClient::notifyCallback, this, _1, _2, _3, _4))) {
NIMBLE_UART_LOGE(LOG_TAG, "Failed to subscribe for notifications");
}
}
if (!subscribe) {
m_rxBufSize = 0; // disable RX if not subscribing
}
return true;
}
void NimBLEStreamClient::deinit() {
if (m_pChr && (m_pChr->canNotify() || m_pChr->canIndicate())) {
m_pChr->unsubscribe();
}
NimBLEStream::end();
m_pChr = nullptr;
}
size_t NimBLEStreamClient::write(const uint8_t* data, size_t len) {
if (!m_pChr || !data || len == 0) {
return 0;
}
return NimBLEStream::write(data, len);
}
bool NimBLEStreamClient::send(const uint8_t* data, size_t len) {
if (!m_pChr || !data || len == 0) {
return false;
}
return m_pChr->writeValue(data, len, m_writeWithRsp);
}
void NimBLEStreamClient::notifyCallback(NimBLERemoteCharacteristic* pChar, uint8_t* pData, size_t len, bool isNotify) {
if (pData && len > 0) {
pushRx(pData, len);
}
if (m_userNotifyCallback) {
m_userNotifyCallback(pChar, pData, len, isNotify);
}
}
// UART logging support
int uart_log_printfv(const char* format, va_list arg) {
static char loc_buf[64];
char* temp = loc_buf;
uint32_t len;
va_list copy;
va_copy(copy, arg);
len = vsnprintf(NULL, 0, format, copy);
va_end(copy);
if (len >= sizeof(loc_buf)) {
temp = (char*)malloc(len + 1);
if (temp == NULL) {
return 0;
}
}
int wlen = vsnprintf(temp, len + 1, format, arg);
for (int i = 0; i < wlen; i++) {
uart_tx_one_char(temp[i]);
}
if (len >= sizeof(loc_buf)) {
free(temp);
}
return len;
}
int uart_log_printf(const char* format, ...) {
int len;
va_list arg;
va_start(arg, format);
len = uart_log_printfv(format, arg);
va_end(arg);
return len;
}
# endif // MYNEWT_VAL(BLE_ROLE_CENTRAL)
# endif // CONFIG_BT_NIMBLE_ENABLED && (MYNEWT_VAL(BLE_ROLE_PERIPHERAL) || MYNEWT_VAL(BLE_ROLE_CENTRAL))
#endif // ESP_PLATFORM

220
src/NimBLEStream.h Normal file
View File

@@ -0,0 +1,220 @@
/*
* 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.
*/
#ifdef ESP_PLATFORM
# ifndef NIMBLE_CPP_STREAM_H
# define NIMBLE_CPP_STREAM_H
# include "syscfg/syscfg.h"
# if CONFIG_BT_NIMBLE_ENABLED && (MYNEWT_VAL(BLE_ROLE_PERIPHERAL) || MYNEWT_VAL(BLE_ROLE_CENTRAL))
# include "NimBLEUUID.h"
# include <freertos/FreeRTOS.h>
# include <freertos/ringbuf.h>
# if NIMBLE_CPP_ARDUINO_STRING_AVAILABLE
# include <Stream.h>
# else
// Minimal Stream/Print stubs when Arduino not available
class Print {
public:
virtual ~Print() {}
virtual size_t write(uint8_t) = 0;
virtual size_t write(const uint8_t* buffer, size_t size) = 0;
size_t print(const char* s);
size_t println(const char* s);
size_t printf(const char* format, ...) __attribute__((format(printf, 2, 3)));
};
class Stream : public Print {
public:
virtual int available() = 0;
virtual int read() = 0;
virtual int peek() = 0;
void setTimeout(unsigned long timeout) { m_timeout = timeout; }
unsigned long getTimeout() const { return m_timeout; }
protected:
unsigned long m_timeout{0};
};
# endif
class NimBLEStream : public Stream {
public:
NimBLEStream() = default;
virtual ~NimBLEStream() { end(); }
bool begin();
bool end();
// Configure TX/RX buffer sizes and task parameters before begin()
void setTxBufSize(uint32_t size) { m_txBufSize = size; }
void setRxBufSize(uint32_t size) { m_rxBufSize = size; }
void setTxTaskStackSize(uint32_t size) { m_txTaskStackSize = size; }
void setTxTaskPriority(uint32_t priority) { m_txTaskPriority = priority; }
// Print/Stream TX methods
virtual size_t write(const uint8_t* data, size_t len) override;
virtual size_t write(uint8_t data) override { return write(&data, 1); }
size_t availableForWrite() const;
void flush() override;
// Stream RX methods
virtual int available() override;
virtual int read() override;
virtual int peek() override;
// Serial-like helpers
bool ready() const { return isReady(); }
operator bool() const { return ready(); }
using Print::write;
protected:
static void txTask(void* arg);
virtual bool send(const uint8_t* data, size_t len) = 0;
virtual bool isReady() const = 0;
// Push received data into RX ring (called by subclass callbacks)
size_t pushRx(const uint8_t* data, size_t len);
RingbufHandle_t m_txBuf{nullptr};
RingbufHandle_t m_rxBuf{nullptr};
TaskHandle_t m_txTask{nullptr};
uint32_t m_txTaskStackSize{4096};
uint32_t m_txTaskPriority{tskIDLE_PRIORITY + 1};
uint32_t m_txBufSize{1024};
uint32_t m_rxBufSize{1024};
// RX peek state
mutable uint8_t m_peekByte{0};
mutable bool m_hasPeek{false};
};
# if MYNEWT_VAL(BLE_ROLE_PERIPHERAL)
# include "NimBLECharacteristic.h"
class NimBLEStreamServer : public NimBLEStream {
public:
NimBLEStreamServer() : m_charCallbacks(this) {}
~NimBLEStreamServer() = default;
// non-copyable
NimBLEStreamServer(const NimBLEStreamServer&) = delete;
NimBLEStreamServer& operator=(const NimBLEStreamServer&) = delete;
bool init(const NimBLEUUID& svcUuid = NimBLEUUID(uint16_t(0xc0de)),
const NimBLEUUID& chrUuid = NimBLEUUID(uint16_t(0xfeed)),
bool canWrite = false,
bool secure = false);
void deinit();
size_t write(const uint8_t* data, size_t len) override;
uint16_t getPeerHandle() const { return m_charCallbacks.m_peerHandle; }
bool hasSubscriber() const { return m_charCallbacks.m_peerHandle != BLE_HS_CONN_HANDLE_NONE; }
size_t getMaxLength() const { return m_charCallbacks.m_maxLen; }
void setCallbacks(NimBLECharacteristicCallbacks* pCallbacks) { m_charCallbacks.m_userCallbacks = pCallbacks; }
private:
bool send(const uint8_t* data, size_t len) override;
bool isReady() const override { return hasSubscriber(); }
struct ChrCallbacks : public NimBLECharacteristicCallbacks {
ChrCallbacks(NimBLEStreamServer* parent)
: m_parent(parent), m_userCallbacks(nullptr), m_peerHandle(BLE_HS_CONN_HANDLE_NONE), m_maxLen(0) {}
void onWrite(NimBLECharacteristic* pCharacteristic, NimBLEConnInfo& connInfo) override;
void onSubscribe(NimBLECharacteristic* pCharacteristic, NimBLEConnInfo& connInfo, uint16_t subValue) override;
void onStatus(NimBLECharacteristic* pCharacteristic, int code) override;
// override this to avoid recursion when debug logs are enabled
void onStatus(NimBLECharacteristic* pCharacteristic, NimBLEConnInfo& connInfo, int code) {
if (m_userCallbacks) {
m_userCallbacks->onStatus(pCharacteristic, connInfo, code);
}
}
NimBLEStreamServer* m_parent;
NimBLECharacteristicCallbacks* m_userCallbacks;
uint16_t m_peerHandle;
uint16_t m_maxLen;
} m_charCallbacks;
NimBLECharacteristic* m_pChr{nullptr};
int m_rc{0};
};
# endif // BLE_ROLE_PERIPHERAL
# if MYNEWT_VAL(BLE_ROLE_CENTRAL)
# include "NimBLERemoteCharacteristic.h"
class NimBLEStreamClient : public NimBLEStream {
public:
NimBLEStreamClient() = default;
~NimBLEStreamClient() = default;
// non-copyable
NimBLEStreamClient(const NimBLEStreamClient&) = delete;
NimBLEStreamClient& operator=(const NimBLEStreamClient&) = delete;
// Attach a discovered remote characteristic; app owns discovery/connection.
// Set subscribeNotify=true to receive notifications into RX buffer.
bool init(NimBLERemoteCharacteristic* pChr, bool subscribeNotify = false);
void deinit();
size_t write(const uint8_t* data, size_t len) override;
void setWriteWithResponse(bool useWithRsp) { m_writeWithRsp = useWithRsp; }
void setNotifyCallback(NimBLERemoteCharacteristic::notify_callback cb) { m_userNotifyCallback = cb; }
private:
bool send(const uint8_t* data, size_t len) override;
bool isReady() const override { return m_pChr != nullptr; }
void notifyCallback(NimBLERemoteCharacteristic* pChar, uint8_t* pData, size_t len, bool isNotify);
NimBLERemoteCharacteristic* m_pChr{nullptr};
bool m_writeWithRsp{false};
NimBLERemoteCharacteristic::notify_callback m_userNotifyCallback{nullptr};
};
# endif // BLE_ROLE_CENTRAL
# endif // CONFIG_BT_NIMBLE_ENABLED && (MYNEWT_VAL(BLE_ROLE_PERIPHERAL) || MYNEWT_VAL(BLE_ROLE_CENTRAL))
// These logging macros exist to provide log output over UART so that it stream classes can
// be used to redirect logs without causing recursion issues.
static int uart_log_printfv(const char* format, va_list arg);
static int uart_log_printf(const char* format, ...);
# if MYNEWT_VAL(NIMBLE_CPP_LOG_LEVEL) >= 4
# define NIMBLE_UART_LOGD(tag, format, ...) uart_log_printf("D %s: " format "\n", tag, ##__VA_ARGS__)
# else
# define NIMBLE_UART_LOGD(tag, format, ...) (void)tag
# endif
# if MYNEWT_VAL(NIMBLE_CPP_LOG_LEVEL) >= 3
# define NIMBLE_UART_LOGI(tag, format, ...) uart_log_printf("I %s: " format "\n", tag, ##__VA_ARGS__)
# else
# define NIMBLE_UART_LOGI(tag, format, ...) (void)tag
# endif
# if MYNEWT_VAL(NIMBLE_CPP_LOG_LEVEL) >= 2
# define NIMBLE_UART_LOGW(tag, format, ...) uart_log_printf("W %s: " format "\n", tag, ##__VA_ARGS__)
# else
# define NIMBLE_UART_LOGW(tag, format, ...) (void)tag
# endif
# if MYNEWT_VAL(NIMBLE_CPP_LOG_LEVEL) >= 1
# define NIMBLE_UART_LOGE(tag, format, ...) uart_log_printf("E %s: " format "\n", tag, ##__VA_ARGS__)
# else
# define NIMBLE_UART_LOGE(tag, format, ...) (void)tag
# endif
# endif // NIMBLE_CPP_STREAM_H
#endif // ESP_PLATFORM

View File

@@ -1,22 +1,24 @@
/*
* NimBLEUUID.cpp
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Created: on Jan 24 2020
* Author H2zero
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* Originally:
* http://www.apache.org/licenses/LICENSE-2.0
*
* BLEUUID.cpp
*
* Created on: Jun 21, 2017
* Author: kolban
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include "NimBLEUUID.h"
#if CONFIG_BT_NIMBLE_ENABLED
# include "NimBLEUtils.h"
# include "NimBLEUUID.h"
# include "NimBLELog.h"
/**** FIX COMPILATION ****/
@@ -334,4 +336,4 @@ NimBLEUUID::operator std::string() const {
return ble_uuid_to_str(&m_uuid.u, buf);
} // operator std::string
# endif /* CONFIG_BT_ENABLED */
#endif /* CONFIG_BT_NIMBLE_ENABLED */

View File

@@ -1,22 +1,25 @@
/*
* NimBLEUUID.h
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Created: on Jan 24 2020
* Author H2zero
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* Originally:
* http://www.apache.org/licenses/LICENSE-2.0
*
* BLEUUID.h
*
* Created on: Jun 21, 2017
* Author: kolban
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef NIMBLE_CPP_UUID_H_
#define NIMBLE_CPP_UUID_H_
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include "syscfg/syscfg.h"
#if CONFIG_BT_NIMBLE_ENABLED
# if defined(CONFIG_NIMBLE_CPP_IDF)
# include "host/ble_uuid.h"
@@ -61,11 +64,11 @@ class NimBLEUUID {
bool operator==(const NimBLEUUID& rhs) const;
bool operator!=(const NimBLEUUID& rhs) const;
operator std::string() const;
operator std::string() const;
private:
ble_uuid_any_t m_uuid{};
}; // NimBLEUUID
#endif /* CONFIG_BT_ENABLED */
#endif /* NIMBLE_CPP_UUID_H_ */
#endif // CONFIG_BT_NIMBLE_ENABLED
#endif // NIMBLE_CPP_UUID_H_

View File

@@ -1,15 +1,23 @@
/*
* NimBLEUtils.cpp
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Created: on Jan 25 2020
* Author H2zero
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include "NimBLEUtils.h"
#if CONFIG_BT_NIMBLE_ENABLED
# include "NimBLEUtils.h"
# include "NimBLEAddress.h"
# include "NimBLELog.h"
@@ -27,11 +35,42 @@
# include <stdlib.h>
# include <climits>
# if defined INC_FREERTOS_H
constexpr uint32_t TASK_BLOCK_BIT = (1 << CONFIG_NIMBLE_CPP_FREERTOS_TASK_BLOCK_BIT);
# ifndef MYNEWT_VAL_NIMBLE_CPP_ENABLE_RETURN_CODE_TEXT
# ifdef CONFIG_NIMBLE_CPP_ENABLE_RETURN_CODE_TEXT
# define MYNEWT_VAL_NIMBLE_CPP_ENABLE_RETURN_CODE_TEXT CONFIG_NIMBLE_CPP_ENABLE_RETURN_CODE_TEXT
# else
# define MYNEWT_VAL_NIMBLE_CPP_ENABLE_RETURN_CODE_TEXT (0)
# endif
# endif
static const char* LOG_TAG = "NimBLEUtils";
# ifndef MYNEWT_VAL_NIMBLE_CPP_ENABLE_GAP_EVENT_CODE_TEXT
# ifdef CONFIG_NIMBLE_CPP_ENABLE_GAP_EVENT_CODE_TEXT
# define MYNEWT_VAL_NIMBLE_CPP_ENABLE_GAP_EVENT_CODE_TEXT CONFIG_NIMBLE_CPP_ENABLE_GAP_EVENT_CODE_TEXT
# else
# define MYNEWT_VAL_NIMBLE_CPP_ENABLE_GAP_EVENT_CODE_TEXT (0)
# endif
# endif
# ifndef MYNEWT_VAL_NIMBLE_CPP_ENABLE_ADVERTISEMENT_TYPE_TEXT
# ifdef CONFIG_NIMBLE_CPP_ENABLE_ADVERTISEMENT_TYPE_TEXT
# define MYNEWT_VAL_NIMBLE_CPP_ENABLE_ADVERTISEMENT_TYPE_TEXT CONFIG_NIMBLE_CPP_ENABLE_ADVERTISEMENT_TYPE_TEXT
# else
# define MYNEWT_VAL_NIMBLE_CPP_ENABLE_ADVERTISEMENT_TYPE_TEXT (0)
# endif
# endif
# if defined INC_FREERTOS_H
# ifndef MYNEWT_VAL_NIMBLE_CPP_FREERTOS_TASK_BLOCK_BIT
# ifndef CONFIG_NIMBLE_CPP_FREERTOS_TASK_BLOCK_BIT
# define MYNEWT_VAL_NIMBLE_CPP_FREERTOS_TASK_BLOCK_BIT 31
# else
# define MYNEWT_VAL_NIMBLE_CPP_FREERTOS_TASK_BLOCK_BIT CONFIG_NIMBLE_CPP_FREERTOS_TASK_BLOCK_BIT
# endif
# endif
# endif
constexpr uint32_t TASK_BLOCK_BIT = (1 << MYNEWT_VAL(NIMBLE_CPP_FREERTOS_TASK_BLOCK_BIT));
static const char* LOG_TAG = "NimBLEUtils";
/**
* @brief Construct a NimBLETaskData instance.
@@ -43,7 +82,7 @@ NimBLETaskData::NimBLETaskData(void* pInstance, int flags, void* buf)
: m_pInstance{pInstance},
m_flags{flags},
m_pBuf{buf}
# if defined INC_FREERTOS_H
# ifdef INC_FREERTOS_H
,
m_pHandle{xTaskGetCurrentTaskHandle()} {
}
@@ -64,8 +103,9 @@ NimBLETaskData::NimBLETaskData(void* pInstance, int flags, void* buf)
* @brief Destructor.
*/
NimBLETaskData::~NimBLETaskData() {
# if !defined INC_FREERTOS_H
# ifndef INC_FREERTOS_H
if (m_pHandle != nullptr) {
ble_npl_sem_deinit(static_cast<ble_npl_sem*>(m_pHandle));
delete static_cast<ble_npl_sem*>(m_pHandle);
}
# endif
@@ -85,7 +125,7 @@ bool NimBLEUtils::taskWait(const NimBLETaskData& taskData, uint32_t timeout) {
ble_npl_time_ms_to_ticks(timeout, &ticks);
}
# if defined INC_FREERTOS_H
# ifdef INC_FREERTOS_H
uint32_t notificationValue;
xTaskNotifyWait(0, TASK_BLOCK_BIT, &notificationValue, 0);
if (notificationValue & TASK_BLOCK_BIT) {
@@ -107,7 +147,7 @@ bool NimBLEUtils::taskWait(const NimBLETaskData& taskData, uint32_t timeout) {
void NimBLEUtils::taskRelease(const NimBLETaskData& taskData, int flags) {
taskData.m_flags = flags;
if (taskData.m_pHandle != nullptr) {
# if defined INC_FREERTOS_H
# ifdef INC_FREERTOS_H
xTaskNotify(static_cast<TaskHandle_t>(taskData.m_pHandle), TASK_BLOCK_BIT, eSetBits);
# else
ble_npl_sem_release(static_cast<ble_npl_sem*>(taskData.m_pHandle));
@@ -121,7 +161,7 @@ void NimBLEUtils::taskRelease(const NimBLETaskData& taskData, int flags) {
* @return A string representation of the return code.
*/
const char* NimBLEUtils::returnCodeToString(int rc) {
# if defined(CONFIG_NIMBLE_CPP_ENABLE_RETURN_CODE_TEXT)
# if MYNEWT_VAL(NIMBLE_CPP_ENABLE_RETURN_CODE_TEXT)
switch (rc) {
case 0:
return "SUCCESS";
@@ -404,10 +444,10 @@ const char* NimBLEUtils::returnCodeToString(int rc) {
default:
return "Unknown";
}
# else // #if defined(CONFIG_NIMBLE_CPP_ENABLE_RETURN_CODE_TEXT)
# else // MYNEWT_VAL(NIMBLE_CPP_ENABLE_RETURN_CODE_TEXT)
(void)rc;
return "";
# endif // #if defined(CONFIG_NIMBLE_CPP_ENABLE_RETURN_CODE_TEXT)
# endif // MYNEWT_VAL(NIMBLE_CPP_ENABLE_RETURN_CODE_TEXT)
}
/**
@@ -416,7 +456,7 @@ const char* NimBLEUtils::returnCodeToString(int rc) {
* @return A string representation of the advertising flags.
*/
const char* NimBLEUtils::advTypeToString(uint8_t advType) {
# if defined(CONFIG_NIMBLE_CPP_ENABLE_ADVERTISEMENT_TYPE_TEXT)
# if MYNEWT_VAL(NIMBLE_CPP_ENABLE_ADVERTISEMENT_TYPE_TEXT)
switch (advType) {
case BLE_HCI_ADV_TYPE_ADV_IND: // 0
return "Undirected - Connectable / Scannable";
@@ -431,10 +471,10 @@ const char* NimBLEUtils::advTypeToString(uint8_t advType) {
default:
return "Unknown flag";
}
# else // #if defined(CONFIG_NIMBLE_CPP_ENABLE_ADVERTISEMENT_TYPE_TEXT)
# else // MYNEWT_VAL(NIMBLE_CPP_ENABLE_ADVERTISEMENT_TYPE_TEXT)
(void)advType;
return "";
# endif // #if defined(CONFIG_NIMBLE_CPP_ENABLE_ADVERTISEMENT_TYPE_TEXT)
# endif // MYNEWT_VAL(NIMBLE_CPP_ENABLE_ADVERTISEMENT_TYPE_TEXT)
} // adFlagsToString
/**
@@ -443,7 +483,7 @@ const char* NimBLEUtils::advTypeToString(uint8_t advType) {
* @return A string representation of the event type.
*/
const char* NimBLEUtils::gapEventToString(uint8_t eventType) {
# if defined(CONFIG_NIMBLE_CPP_ENABLE_GAP_EVENT_CODE_TEXT)
# if MYNEWT_VAL(NIMBLE_CPP_ENABLE_GAP_EVENT_CODE_TEXT)
switch (eventType) {
case BLE_GAP_EVENT_CONNECT: // 0
return "BLE_GAP_EVENT_CONNECT ";
@@ -498,22 +538,24 @@ const char* NimBLEUtils::gapEventToString(uint8_t eventType) {
return "BLE_GAP_EVENT_PATHLOSS_THRESHOLD";
case BLE_GAP_EVENT_TRANSMIT_POWER: // 26
return "BLE_GAP_EVENT_TRANSMIT_POWER";
case BLE_GAP_EVENT_SUBRATE_CHANGE: // 27
case BLE_GAP_EVENT_PARING_COMPLETE: // 27
return "BLE_GAP_EVENT_PARING_COMPLETE";
case BLE_GAP_EVENT_SUBRATE_CHANGE: // 28
return "BLE_GAP_EVENT_SUBRATE_CHANGE";
case BLE_GAP_EVENT_VS_HCI: // 28
case BLE_GAP_EVENT_VS_HCI: // 29
return "BLE_GAP_EVENT_VS_HCI";
case BLE_GAP_EVENT_REATTEMPT_COUNT: // 29
case BLE_GAP_EVENT_REATTEMPT_COUNT: // 31
return "BLE_GAP_EVENT_REATTEMPT_COUNT";
case BLE_GAP_EVENT_AUTHORIZE: // 30
case BLE_GAP_EVENT_AUTHORIZE: // 32
return "BLE_GAP_EVENT_AUTHORIZE";
case BLE_GAP_EVENT_TEST_UPDATE: // 31
case BLE_GAP_EVENT_TEST_UPDATE: // 33
return "BLE_GAP_EVENT_TEST_UPDATE";
# ifdef BLE_GAP_EVENT_DATA_LEN_CHG
case BLE_GAP_EVENT_DATA_LEN_CHG: // 32
case BLE_GAP_EVENT_DATA_LEN_CHG: // 34
return "BLE_GAP_EVENT_DATA_LEN_CHG";
# endif
# ifdef BLE_GAP_EVENT_LINK_ESTAB
case BLE_GAP_EVENT_LINK_ESTAB: // 33
case BLE_GAP_EVENT_LINK_ESTAB: // 38
return "BLE_GAP_EVENT_LINK_ESTAB";
# endif
# endif
@@ -521,10 +563,10 @@ const char* NimBLEUtils::gapEventToString(uint8_t eventType) {
NIMBLE_LOGD(LOG_TAG, "Unknown event type %d 0x%.2x", eventType, eventType);
return "Unknown event type";
}
# else // #if defined(CONFIG_NIMBLE_CPP_ENABLE_GAP_EVENT_CODE_TEXT)
# else // MYNEWT_VAL(NIMBLE_CPP_ENABLE_GAP_EVENT_CODE_TEXT)
(void)eventType;
return "";
# endif // #if defined(CONFIG_NIMBLE_CPP_ENABLE_GAP_EVENT_CODE_TEXT)
# endif // MYNEWT_VAL(NIMBLE_CPP_ENABLE_GAP_EVENT_CODE_TEXT)
} // gapEventToString
/**
@@ -562,4 +604,4 @@ NimBLEAddress NimBLEUtils::generateAddr(bool nrpa) {
return NimBLEAddress{addr};
} // generateAddr
#endif // CONFIG_BT_ENABLED
#endif // CONFIG_BT_NIMBLE_ENABLED

View File

@@ -1,16 +1,46 @@
/*
* NimBLEUtils.h
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Created: on Jan 25 2020
* Author H2zero
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef NIMBLE_CPP_UTILS_H_
#define NIMBLE_CPP_UTILS_H_
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include "syscfg/syscfg.h"
#if CONFIG_BT_NIMBLE_ENABLED
# ifndef MYNEWT_VAL_NIMBLE_CPP_DEBUG_ASSERT_ENABLED
# if defined(CONFIG_NIMBLE_CPP_DEBUG_ASSERT_ENABLED)
# define MYNEWT_VAL_NIMBLE_CPP_DEBUG_ASSERT_ENABLED CONFIG_NIMBLE_CPP_DEBUG_ASSERT_ENABLED
# else
# define MYNEWT_VAL_NIMBLE_CPP_DEBUG_ASSERT_ENABLED (0)
# endif
# endif
#if MYNEWT_VAL(NIMBLE_CPP_DEBUG_ASSERT_ENABLED) && !defined NDEBUG
void nimble_cpp_assert(const char *file, unsigned line) __attribute((weak, noreturn));
# define NIMBLE_ATT_VAL_FILE (__builtin_strrchr(__FILE__, '/') ? \
__builtin_strrchr (__FILE__, '/') + 1 : __FILE__)
# define NIMBLE_CPP_DEBUG_ASSERT(cond) \
if (!(cond)) { \
nimble_cpp_assert(NIMBLE_ATT_VAL_FILE, __LINE__); \
}
#else
# define NIMBLE_CPP_DEBUG_ASSERT(cond) (void(0))
#endif
# include <string>
class NimBLEAddress;
@@ -46,5 +76,5 @@ class NimBLEUtils {
static void taskRelease(const NimBLETaskData& taskData, int rc = 0);
};
#endif // CONFIG_BT_ENABLED
#endif // CONFIG_BT_NIMBLE_ENABLED
#endif // NIMBLE_CPP_UTILS_H_

View File

@@ -0,0 +1,86 @@
/*
* 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_VALUE_ATTRIBUTE_H_
#define NIMBLE_CPP_VALUE_ATTRIBUTE_H_
#include "syscfg/syscfg.h"
#if CONFIG_BT_NIMBLE_ENABLED && (MYNEWT_VAL(BLE_ROLE_PERIPHERAL) || MYNEWT_VAL(BLE_ROLE_CENTRAL))
# include "NimBLEAttribute.h"
# include "NimBLEAttValue.h"
class NimBLEValueAttribute {
public:
NimBLEValueAttribute(uint16_t maxLen = BLE_ATT_ATTR_MAX_LEN, uint16_t initLen = MYNEWT_VAL(NIMBLE_CPP_ATT_VALUE_INIT_LENGTH))
: m_value(initLen, maxLen) {}
/**
* @brief Get a copy of the value of the attribute value.
* @param [in] timestamp (Optional) A pointer to a time_t struct to get the time the value set.
* @return A copy of the attribute value.
*/
NimBLEAttValue getValue(time_t* timestamp) const { return m_value.getValue(timestamp); }
/**
* @brief Get a copy of the value of the attribute value.
* @return A copy of the attribute value.
*/
NimBLEAttValue getValue() const { return m_value; }
/**
* @brief Get the length of the attribute value.
* @return The length of the attribute value.
*/
size_t getLength() const { return m_value.size(); }
/**
* @brief Template to convert the data to <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>(&timestamp, skipSizeCheck);</tt>
* Used for types that are trivially copyable and convertible to NimBLEAttValue.
*/
template <typename T>
typename std::enable_if<std::is_trivially_copyable<T>::value, T>::type
getValue(time_t* timestamp = nullptr, bool skipSizeCheck = false) const {
return m_value.getValue<T>(timestamp, skipSizeCheck);
}
/**
* @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>(&timestamp, skipSizeCheck);</tt>
* Used for types that are not trivially copyable and convertible to NimBLEAttValue via it's operators.
*/
template <typename T>
typename std::enable_if<!std::is_trivially_copyable<T>::value && std::is_convertible<T, NimBLEAttValue>::value, T>::type
getValue(time_t* timestamp = nullptr, bool skipSizeCheck = false) const {
return m_value;
}
protected:
NimBLEAttValue m_value{};
};
#endif // CONFIG_BT_NIMBLE_ENABLED && (MYNEWT_VAL(BLE_ROLE_PERIPHERAL) || MYNEWT_VAL(BLE_ROLE_CENTRAL))
#endif // NIMBLE_CPP_ATTRIBUTE_H_

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