Compare commits

...

57 Commits

Author SHA1 Message Date
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
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
a57c45e1de Fix build with idf versions < 5.x 2025-06-27 18:07:41 -06:00
edfc838bef [Bugfix] allow peripheral and central roles without broadcast/scan. 2025-06-20 10:41:28 -06:00
f1ead9959d Bump idf_component version 2025-06-11 11:40:42 -06:00
b30421c19d Fix library.json version 2025-06-11 11:34:10 -06:00
bdb868d125 Release 2.3.1 2025-06-11 11:14:52 -06:00
503939c66f Update docs 2025-06-11 11:11:58 -06:00
2640c44b45 Support passing data directly from NimBLEBeacon.getData() to NimBLEAdvertisementData.setManufacturerData() 2025-06-11 11:11:11 -06:00
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
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
39f974625c Fix builds when exluding roles 2025-06-02 18:06:40 -06:00
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
067274973d Release 2.3.0 2025-05-19 16:54:59 -06:00
30877344bd Add support for esp32c5 2025-05-19 14:50:28 -06:00
68dd0b37b5 [Bugfix] Attribute getValue fails with some data types 2025-05-19 14:28:53 -06:00
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
e29d1ce3cf Refactor L2Cap (style) 2025-05-05 18:30:37 -06:00
a5702746c5 Add missed pointer parameter to const ref fix 2025-05-05 18:11:01 -06:00
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
80def6663b Fix preprocessor statement 2025-05-05 14:38:26 -06:00
7a8d10bb71 Set max connections correctly for esp32s3/c3 2025-05-05 14:38:26 -06:00
ce1b8bc2ec Fix build with non-esp devices 2025-05-05 14:38:07 -06:00
dadbc0d423 [Bugfix] Correct NimBLEClient array initialization. 2025-05-03 15:00:43 -06:00
da31f90436 Fix build when host based privacy is enabled without connection roles 2025-05-03 14:59:24 -06:00
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
59e111ab55 Allow PHY updates without enabling extended advertising. 2025-04-25 09:07:23 -06:00
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
e18d78678f [Bugfix] Explicit getValue template returning nil value with convertable types. 2025-04-24 17:07:42 -06:00
54ec3f4c92 [Bugfix] Clear attribute value when zero length value is written. 2025-04-24 17:07:20 -06:00
9ac9551a2b fix(Utils): Update gapEventToString switch case 2025-04-22 16:08:57 -06:00
0172cb4b39 Only ignore scan discovery events when scan is stopped. 2025-04-14 08:52:32 -06:00
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
033c7c27ad Fix extended server example - data too long error 2025-04-13 16:06:34 -06:00
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
01976cec54 [Bugfix] Incorrect onSubscribe value when indications are set. 2025-03-28 16:56:54 -06:00
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
148087c508 Fix ESP32-P4 compile when using Arduino as an ESP-IDF component 2025-03-02 21:46:34 +01:00
88c5d0c7b7 Bump docs version 2025-02-28 14:56:46 -07:00
a6c03a2aaa Release 2.2.1 2025-02-28 09:51:31 -07:00
9df8445241 [Bugfix] Check if remote descriptor vector has increased. 2025-02-25 17:08:01 -07:00
e5f4d52a5a Update changelog 2025-02-25 14:26:57 -07:00
7ef247c8db Partially revert Commit 052c0a04 to restore client connect with device parameter. 2025-02-25 14:23:25 -07:00
c582c8c67e Release 2.2.0 2025-02-24 17:58:21 -07:00
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
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
b6379848ae change(2904|AdvData): Add missing parameter name in declarations 2025-02-05 15:15:49 -07:00
b29919df96 feat(Device): Implement store status handler 2025-02-05 15:14:42 -07:00
052c0a0455 feat(AdvDevice): Add convenience operator to NimBLEAddress 2025-02-05 14:07:10 -07:00
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
5490cef129 feat(Log): Add macros for conditional log print and rc handling 2025-01-28 13:40:19 -05:00
459e8c9fcd Release 2.1.1 2025-01-26 18:23:11 -07:00
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
8130f88be8 Rename config macros to enable duplicate scan options on s3/c3 2025-01-26 11:09:36 -07:00
c39e288c3e Workaround for P4 CI build error. 2025-01-26 09:41:25 -07:00
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
57fe9cb77f Release 2.1.0 2025-01-12 19:17:10 -07:00
85 changed files with 1748 additions and 600 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", "v5.3.2"]
idf_target: ["esp32", "esp32s3", "esp32c2", "esp32c3", "esp32c6", "esp32h2", "esp32p4"]
idf_ver: ["release-v4.4", "release-v5.4"]
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

View File

@ -1,6 +1,75 @@
# Changelog
All notable changes to this project will be documented in this file.
## [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

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"

2
NOTICE
View File

@ -1,6 +1,6 @@
esp-nimble-cpp
NimBLE-Arduino
Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
esp-nimble-cpp, NimBLE-Arduino contributors.
The Initial Developer of some parts of this library, which are copied from,

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
@ -133,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.3
PROJECT_NUMBER = 2.3.1
# 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.

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

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

@ -261,8 +261,7 @@ 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);
/** Optional: set the transmit power */
NimBLEDevice::setPower(3); /** 3dbm */

View File

@ -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,5 +1,5 @@
## IDF Component Manager Manifest File
version: "2.0.3"
version: "2.3.1"
license: "Apache-2.0"
description: "C++ wrapper for the NimBLE BLE stack"
url: "https://github.com/h2zero/esp-nimble-cpp"
@ -16,7 +16,7 @@ dependencies:
rules:
- if: "target in [esp32p4]"
espressif/esp_wifi_remote:
version: "*"
version: ">=0.5.3"
rules:
- if: "target in [esp32p4]"
idf:

View File

@ -1,6 +1,6 @@
{
"name": "esp-nimble-cpp",
"version": "2.0.3",
"version": "2.3.1",
"description": "C++ wrapper for the NimBLE BLE stack",
"keywords": [
"BLE",

View File

@ -1,5 +1,5 @@
/*
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -15,10 +15,8 @@
* limitations under the License.
*/
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
# include "NimBLE2904.h"
#include "NimBLE2904.h"
#if CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL
NimBLE2904::NimBLE2904(NimBLECharacteristic* pChr)
: NimBLEDescriptor(NimBLEUUID((uint16_t)0x2904), BLE_GATT_CHR_F_READ, sizeof(NimBLE2904Data), pChr) {
@ -71,4 +69,4 @@ void NimBLE2904::setUnit(uint16_t unit) {
setValue(m_data);
} // setUnit
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL */
#endif // CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL

View File

@ -1,5 +1,5 @@
/*
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -19,7 +19,7 @@
#define NIMBLE_CPP_2904_H_
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#if CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL
# include "NimBLEDescriptor.h"
@ -68,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);

View File

@ -1,5 +1,5 @@
/*
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -15,10 +15,9 @@
* limitations under the License.
*/
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include "NimBLEAddress.h"
#if CONFIG_BT_ENABLED
# include "NimBLEAddress.h"
# include "NimBLELog.h"
# include <algorithm>
@ -224,11 +223,16 @@ NimBLEAddress::operator std::string() const {
snprintf(buffer,
sizeof(buffer),
NIMBLE_CPP_ADDR_FMT,
this->val[5], NIMBLE_CPP_ADDR_DELIMITER,
this->val[4], NIMBLE_CPP_ADDR_DELIMITER,
this->val[3], NIMBLE_CPP_ADDR_DELIMITER,
this->val[2], NIMBLE_CPP_ADDR_DELIMITER,
this->val[1], NIMBLE_CPP_ADDR_DELIMITER,
this->val[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

View File

@ -1,5 +1,5 @@
/*
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -17,8 +17,9 @@
#ifndef NIMBLE_CPP_ADDRESS_H_
#define NIMBLE_CPP_ADDRESS_H_
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED)
#if CONFIG_BT_ENABLED
# if defined(CONFIG_NIMBLE_CPP_IDF)
# include "nimble/ble.h"
@ -66,5 +67,5 @@ class NimBLEAddress : private ble_addr_t {
operator uint64_t() const;
};
#endif /* CONFIG_BT_ENABLED */
#endif /* NIMBLE_CPP_ADDRESS_H_ */
#endif // CONFIG_BT_ENABLED
#endif // NIMBLE_CPP_ADDRESS_H_

View File

@ -1,5 +1,5 @@
/*
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -15,11 +15,10 @@
* limitations under the License.
*/
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
#include "NimBLEAdvertisedDevice.h"
#if CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_OBSERVER
# include "NimBLEDevice.h"
# include "NimBLEAdvertisedDevice.h"
# include "NimBLEUtils.h"
# include "NimBLELog.h"
@ -780,6 +779,16 @@ bool NimBLEAdvertisedDevice::isLegacyAdvertisement() const {
# 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.
@ -804,4 +813,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_ENABLED && CONFIG_BT_NIMBLE_ROLE_OBSERVER

View File

@ -1,5 +1,5 @@
/*
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -19,7 +19,7 @@
#define NIMBLE_CPP_ADVERTISED_DEVICE_H_
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
#if CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_OBSERVER
# include "NimBLEAddress.h"
# include "NimBLEScan.h"
@ -93,6 +93,7 @@ class NimBLEAdvertisedDevice {
uint8_t getSecondaryPhy() const;
uint16_t getPeriodicInterval() const;
# endif
operator NimBLEAddress() const;
const std::vector<uint8_t>& getPayload() const;
const std::vector<uint8_t>::const_iterator begin() const;

View File

@ -1,5 +1,5 @@
/*
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -15,11 +15,9 @@
* 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_ENABLED && CONFIG_BT_NIMBLE_ROLE_BROADCASTER && !CONFIG_BT_NIMBLE_EXT_ADV) || defined(_DOXYGEN_)
# include "NimBLEAdvertisementData.h"
# include "NimBLEDevice.h"
# include "NimBLEUtils.h"
# include "NimBLEUUID.h"
@ -585,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_ENABLED && CONFIG_BT_NIMBLE_ROLE_BROADCASTER && !CONFIG_BT_NIMBLE_EXT_ADV) || defined(_DOXYGEN_)

View File

@ -1,5 +1,5 @@
/*
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -19,8 +19,7 @@
#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_)
#if (CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_BROADCASTER && !CONFIG_BT_NIMBLE_EXT_ADV) || defined(_DOXYGEN_)
# include <cstdint>
# include <string>
@ -38,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);
@ -75,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_ENABLED && CONFIG_BT_NIMBLE_ROLE_BROADCASTER && !CONFIG_BT_NIMBLE_EXT_ADV) || defined(_DOXYGEN_)
#endif // NIMBLE_CPP_ADVERTISEMENT_DATA_H_

View File

@ -1,5 +1,5 @@
/*
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -15,16 +15,14 @@
* 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_ENABLED && CONFIG_BT_NIMBLE_ROLE_BROADCASTER && !CONFIG_BT_NIMBLE_EXT_ADV) || defined(_DOXYGEN_)
# if defined(CONFIG_NIMBLE_CPP_IDF)
# include "services/gap/ble_svc_gap.h"
# else
# include "nimble/nimble/host/services/gap/include/services/gap/ble_svc_gap.h"
# endif
# include "NimBLEAdvertising.h"
# include "NimBLEDevice.h"
# include "NimBLEServer.h"
# include "NimBLEUtils.h"
@ -44,7 +42,7 @@ NimBLEAdvertising::NimBLEAdvertising()
m_duration{BLE_HS_FOREVER},
m_scanResp{false},
m_advDataSet{false} {
# if !defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
# if !CONFIG_BT_NIMBLE_ROLE_PERIPHERAL
m_advParams.conn_mode = BLE_GAP_CONN_MODE_NON;
# else
m_advParams.conn_mode = BLE_GAP_CONN_MODE_UND;
@ -197,7 +195,7 @@ bool NimBLEAdvertising::start(uint32_t duration, const NimBLEAddress* dirAddr) {
return true;
}
# if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
# if CONFIG_BT_NIMBLE_ROLE_PERIPHERAL
NimBLEServer* pServer = NimBLEDevice::getServer();
if (pServer != nullptr) {
pServer->start(); // make sure the GATT server is ready before advertising
@ -222,7 +220,7 @@ bool NimBLEAdvertising::start(uint32_t duration, const NimBLEAddress* dirAddr) {
duration = BLE_HS_FOREVER;
}
# if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
# if CONFIG_BT_NIMBLE_ROLE_PERIPHERAL
int rc = ble_gap_adv_start(NimBLEDevice::m_ownAddrType,
(dirAddr != nullptr) ? dirAddr->getBase() : NULL,
duration,
@ -622,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_ENABLED && CONFIG_BT_NIMBLE_ROLE_BROADCASTER && !CONFIG_BT_NIMBLE_EXT_ADV) || defined(_DOXYGEN_)

View File

@ -1,5 +1,5 @@
/*
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -19,8 +19,7 @@
#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_)
#if (CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_BROADCASTER && !CONFIG_BT_NIMBLE_EXT_ADV) || defined(_DOXYGEN_)
# if defined(CONFIG_NIMBLE_CPP_IDF)
# include "host/ble_gap.h"
@ -106,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_ENABLED && CONFIG_BT_NIMBLE_ROLE_BROADCASTER && !CONFIG_BT_NIMBLE_EXT_ADV) || defined(_DOXYGEN_)
#endif // NIMBLE_CPP_ADVERTISING_H_

View File

@ -1,5 +1,5 @@
/*
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -15,8 +15,8 @@
* limitations under the License.
*/
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include "NimBLEAttValue.h"
#if CONFIG_BT_ENABLED
# if defined(CONFIG_NIMBLE_CPP_IDF)
# include "nimble/nimble_npl.h"
@ -24,7 +24,6 @@
# include "nimble/nimble/include/nimble/nimble_npl.h"
# endif
# include "NimBLEAttValue.h"
# include "NimBLELog.h"
static const char* LOG_TAG = "NimBLEAttValue";
@ -105,7 +104,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;
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -17,8 +17,9 @@
#ifndef NIMBLE_CPP_ATTVALUE_H
#define NIMBLE_CPP_ATTVALUE_H
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED)
#if CONFIG_BT_ENABLED
# ifdef NIMBLE_CPP_ARDUINO_STRING_AVAILABLE
# include <Arduino.h>
@ -305,9 +306,6 @@ 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
*timestamp = m_timestamp;
@ -316,6 +314,9 @@ class NimBLEAttValue {
# endif
}
if (!skipSizeCheck && size() < sizeof(T)) {
return T();
}
return *(reinterpret_cast<const T*>(m_attr_value));
}
@ -362,5 +363,5 @@ class NimBLEAttValue {
# endif
};
#endif /*(CONFIG_BT_ENABLED) */
#endif /* NIMBLE_CPP_ATTVALUE_H_ */
#endif // CONFIG_BT_ENABLED
#endif // NIMBLE_CPP_ATTVALUE_H_

View File

@ -1,5 +1,5 @@
/*
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -19,7 +19,7 @@
#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))
#if CONFIG_BT_ENABLED && (CONFIG_BT_NIMBLE_ROLE_PERIPHERAL || CONFIG_BT_NIMBLE_ROLE_CENTRAL)
# include "NimBLEUUID.h"

View File

@ -1,5 +1,5 @@
/*
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -15,10 +15,9 @@
* limitations under the License.
*/
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
#include "NimBLEBeacon.h"
#if CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_BROADCASTER
# include "NimBLEBeacon.h"
# include "NimBLEUUID.h"
# include "NimBLELog.h"

View File

@ -1,5 +1,5 @@
/*
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -19,11 +19,12 @@
#define NIMBLE_CPP_BEACON_H_
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
#if CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_BROADCASTER
class NimBLEUUID;
# include <cstdint>
# include <vector>
/**
* @brief Representation of a beacon.
@ -40,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();
@ -60,5 +65,5 @@ class NimBLEBeacon {
BeaconData m_beaconData;
}; // NimBLEBeacon
#endif // NIMBLE_CPP_BEACON_H_
#endif // CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL
#endif // NIMBLE_CPP_BEACON_H_

View File

@ -1,5 +1,5 @@
/*
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -15,10 +15,9 @@
* limitations under the License.
*/
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
# include "NimBLECharacteristic.h"
#if CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL
# include "NimBLE2904.h"
# include "NimBLEDevice.h"
# include "NimBLELog.h"
@ -268,17 +267,32 @@ bool NimBLECharacteristic::sendValue(const uint8_t* value, size_t length, bool i
int rc = 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.
os_mbuf* om = nullptr;
if (connHandle != BLE_HS_CONN_HANDLE_NONE) { // only sending to specific peer
om = ble_hs_mbuf_from_flat(value, length);
if (!om) {
rc = BLE_HS_ENOMEM;
goto done;
}
// Null buffer will read the value from the characteristic
if (isNotification) {
rc = ble_gattc_notify_custom(connHandle, m_handle, om);
} else {
rc = ble_gattc_indicate_custom(connHandle, m_handle, om);
}
goto done;
}
// Notify all connected peers unless a specific handle is provided
for (const auto& ch : NimBLEDevice::getServer()->getPeerDevices()) {
// Must re-create the data buffer on each iteration because it is freed by the calls bellow.
os_mbuf* om = ble_hs_mbuf_from_flat(value, length);
om = ble_hs_mbuf_from_flat(value, length);
if (!om) {
NIMBLE_LOGE(LOG_TAG, "<< sendValue: failed to allocate mbuf");
return false;
rc = BLE_HS_ENOMEM;
goto done;
}
if (isNotification) {
@ -286,24 +300,25 @@ bool NimBLECharacteristic::sendValue(const uint8_t* value, size_t length, bool i
} else {
rc = ble_gattc_indicate_custom(ch, m_handle, om);
}
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "<< sendValue: failed to send value, rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc));
break;
}
}
} else if (connHandle != BLE_HS_CONN_HANDLE_NONE) { // only sending to specific peer
} else if (connHandle != BLE_HS_CONN_HANDLE_NONE) {
// Null buffer will read the value from the characteristic
if (isNotification) {
rc = ble_gattc_notify_custom(connHandle, m_handle, NULL);
rc = ble_gattc_notify_custom(connHandle, m_handle, nullptr);
} else {
rc = ble_gattc_indicate_custom(connHandle, m_handle, NULL);
rc = ble_gattc_indicate_custom(connHandle, m_handle, nullptr);
}
} else { // Notify or indicate to all connected peers the characteristic value
ble_gatts_chr_updated(m_handle);
}
return rc == 0;
done:
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "failed to send value, rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc));
return false;
}
return true;
} // sendValue
void NimBLECharacteristic::readEvent(NimBLEConnInfo& connInfo) {
@ -399,4 +414,4 @@ void NimBLECharacteristicCallbacks::onSubscribe(NimBLECharacteristic* pCharacter
NIMBLE_LOGD("NimBLECharacteristicCallbacks", "onSubscribe: default");
}
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL */
#endif // CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL

View File

@ -1,5 +1,5 @@
/*
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -17,8 +17,9 @@
#ifndef NIMBLE_CPP_CHARACTERISTIC_H_
#define NIMBLE_CPP_CHARACTERISTIC_H_
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#if CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL
class NimBLECharacteristicCallbacks;
class NimBLEService;
@ -87,7 +88,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);
@ -133,7 +136,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);
@ -182,8 +187,8 @@ 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);
} else if constexpr (Has_c_str_length<T>::value) {
@ -204,7 +209,7 @@ 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);
@ -249,5 +254,5 @@ class NimBLECharacteristicCallbacks {
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_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL
#endif // NIMBLE_CPP_CHARACTERISTIC_H_

View File

@ -1,5 +1,5 @@
/*
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -15,10 +15,9 @@
* limitations under the License.
*/
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
#include "NimBLEClient.h"
#if CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL
# include "NimBLEClient.h"
# include "NimBLERemoteService.h"
# include "NimBLERemoteCharacteristic.h"
# include "NimBLEDevice.h"
@ -126,6 +125,24 @@ size_t NimBLEClient::deleteService(const NimBLEUUID& uuid) {
return m_svcVec.size();
} // deleteService
# if CONFIG_BT_NIMBLE_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.
@ -141,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.
@ -227,10 +228,15 @@ bool NimBLEClient::connect(const NimBLEAddress& address, bool deleteAttributes,
break;
case BLE_HS_EBUSY:
# if CONFIG_BT_NIMBLE_ROLE_OBSERVER
// Scan was active, stop it through the NimBLEScan API to release any tasks and call the callback.
if (!NimBLEDevice::getScan()->stop()) {
rc = BLE_HS_EUNKNOWN;
}
# else
rc = BLE_HS_EUNKNOWN;
# endif
break;
case BLE_HS_EDONE:
@ -408,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.
@ -450,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.
@ -982,6 +988,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;
@ -1141,7 +1151,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);
@ -1152,7 +1161,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) {
@ -1298,10 +1306,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_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL

View File

@ -1,5 +1,5 @@
/*
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -19,7 +19,7 @@
#define NIMBLE_CPP_CLIENT_H_
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
#if CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL
# if defined(CONFIG_NIMBLE_CPP_IDF)
# include "host/ble_gap.h"
@ -48,10 +48,12 @@ struct NimBLETaskData;
*/
class NimBLEClient {
public:
# if CONFIG_BT_NIMBLE_ROLE_OBSERVER
bool connect(const NimBLEAdvertisedDevice* device,
bool deleteAttributes = true,
bool asyncConnect = false,
bool exchangeMTU = true);
# endif
bool connect(const NimBLEAddress& address, bool deleteAttributes = true, bool asyncConnect = false, bool exchangeMTU = true);
bool connect(bool deleteAttributes = true, bool asyncConnect = false, bool exchangeMTU = true);
bool disconnect(uint8_t reason = BLE_ERR_REM_USER_CONN_TERM);
@ -95,9 +97,9 @@ class NimBLEClient {
# if CONFIG_BT_NIMBLE_EXT_ADV
void setConnectPhy(uint8_t phyMask);
# endif
bool updatePhy(uint8_t txPhysMask, uint8_t rxPhysMask, uint16_t phyOptions = 0);
bool getPhy(uint8_t* txPhy, uint8_t* rxPhy);
# endif
struct Config {
uint8_t deleteCallbacks : 1; // Delete the callback object when the client is deleted.
@ -213,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.
@ -226,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_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL
#endif // NIMBLE_CPP_CLIENT_H_

View File

@ -1,5 +1,5 @@
/*
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -15,8 +15,8 @@
* limitations under the License.
*/
#ifndef NIMBLECONNINFO_H_
#define NIMBLECONNINFO_H_
#ifndef NIMBLE_CPP_CONNINFO_H_
#define NIMBLE_CPP_CONNINFO_H_
#if defined(CONFIG_NIMBLE_CPP_IDF)
# include "host/ble_gap.h"
@ -80,4 +80,5 @@ class NimBLEConnInfo {
NimBLEConnInfo() {};
NimBLEConnInfo(ble_gap_conn_desc desc) { m_desc = desc; }
};
#endif
#endif // NIMBLE_CPP_CONNINFO_H_

View File

@ -1,5 +1,5 @@
/*
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -15,11 +15,10 @@
* limitations under the License.
*/
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#include "NimBLEDescriptor.h"
#if CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL
# include "NimBLEService.h"
# include "NimBLEDescriptor.h"
# include "NimBLELog.h"
# include <string>
@ -148,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_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL

View File

@ -1,5 +1,5 @@
/*
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -19,7 +19,7 @@
#define NIMBLE_CPP_DESCRIPTOR_H_
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#if CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL
# include "NimBLELocalValueAttribute.h"
# include <string>
@ -72,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_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL
#endif // NIMBLE_CPP_DESCRIPTOR_H_

View File

@ -1,5 +1,5 @@
/*
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -15,11 +15,8 @@
* limitations under the License.
*/
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED)
# include "NimBLEDevice.h"
# include "NimBLEUtils.h"
#include "NimBLEDevice.h"
#if CONFIG_BT_ENABLED
# ifdef ESP_PLATFORM
# include "esp_err.h"
@ -59,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";
@ -76,15 +65,21 @@ 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 CONFIG_BT_NIMBLE_ROLE_OBSERVER
NimBLEScan* NimBLEDevice::m_pScan = nullptr;
# endif
# if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
# if CONFIG_BT_NIMBLE_ROLE_PERIPHERAL
NimBLEServer* NimBLEDevice::m_pServer = nullptr;
# if CONFIG_BT_NIMBLE_L2CAP_COC_MAX_NUM > 0
NimBLEL2CAPServer* NimBLEDevice::m_pL2CAPServer = nullptr;
# endif
# endif
# if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
# if CONFIG_BT_NIMBLE_ROLE_BROADCASTER
# if CONFIG_BT_NIMBLE_EXT_ADV
NimBLEExtAdvertising* NimBLEDevice::m_bleAdvertising = nullptr;
# else
@ -92,8 +87,8 @@ 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 CONFIG_BT_NIMBLE_ROLE_CENTRAL
std::array<NimBLEClient*, NIMBLE_MAX_CONNECTIONS> NimBLEDevice::m_pClients{};
# endif
bool NimBLEDevice::m_initialized{false};
@ -104,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
@ -114,7 +118,7 @@ uint8_t NimBLEDevice::m_scanFilterMode{CONFIG_BTDM_SCAN_DUPL_TYPE};
/* SERVER FUNCTIONS */
/* -------------------------------------------------------------------------- */
# if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
# if CONFIG_BT_NIMBLE_ROLE_PERIPHERAL
/**
* @brief Create an instance of a server.
* @return A pointer to the instance of the server.
@ -137,13 +141,34 @@ NimBLEServer* NimBLEDevice::createServer() {
NimBLEServer* NimBLEDevice::getServer() {
return m_pServer;
} // getServer
# endif // #if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
# if CONFIG_BT_NIMBLE_L2CAP_COC_MAX_NUM
/**
* @brief Create an instance of a L2CAP server.
* @return A pointer to the instance of the L2CAP server.
*/
NimBLEL2CAPServer* NimBLEDevice::createL2CAPServer() {
if (NimBLEDevice::m_pL2CAPServer == nullptr) {
NimBLEDevice::m_pL2CAPServer = new NimBLEL2CAPServer();
}
return m_pL2CAPServer;
} // createL2CAPServer
/**
* @brief Get the instance of the L2CAP server.
* @return A pointer to the L2CAP server instance or nullptr if none have been created.
*/
NimBLEL2CAPServer* NimBLEDevice::getL2CAPServer() {
return m_pL2CAPServer;
} // getL2CAPServer
# endif // #if CONFIG_BT_NIMBLE_L2CAP_COC_MAX_NUM
# endif // #if CONFIG_BT_NIMBLE_ROLE_PERIPHERAL
/* -------------------------------------------------------------------------- */
/* ADVERTISING FUNCTIONS */
/* -------------------------------------------------------------------------- */
# if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
# if CONFIG_BT_NIMBLE_ROLE_BROADCASTER
# if CONFIG_BT_NIMBLE_EXT_ADV
/**
* @brief Get the instance of the extended advertising object.
@ -207,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 CONFIG_BT_NIMBLE_ROLE_BROADCASTER
/* -------------------------------------------------------------------------- */
/* SCAN FUNCTIONS */
@ -218,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 CONFIG_BT_NIMBLE_ROLE_OBSERVER
NimBLEScan* NimBLEDevice::getScan() {
if (m_pScan == nullptr) {
m_pScan = new NimBLEScan();
@ -228,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
@ -275,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 // CONFIG_BT_NIMBLE_ROLE_OBSERVER
/* -------------------------------------------------------------------------- */
/* CLIENT FUNCTIONS */
/* -------------------------------------------------------------------------- */
# if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
# if CONFIG_BT_NIMBLE_ROLE_CENTRAL
/**
* @brief Creates a new client object, each client can connect to 1 peripheral device.
* @return A pointer to the new client object, or nullptr on error.
@ -418,7 +462,7 @@ std::vector<NimBLEClient*> NimBLEDevice::getConnectedClients() {
return clients;
} // getConnectedClients
# endif // #if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
# endif // CONFIG_BT_NIMBLE_ROLE_CENTRAL
/* -------------------------------------------------------------------------- */
/* TRANSMIT POWER */
@ -473,14 +517,14 @@ bool NimBLEDevice::setPower(int8_t dbm, NimBLETxPowerType type) {
dbm++; // round up to the next multiple of 3 to be able to target 20dbm
}
bool success = false;
esp_power_level_t espPwr = static_cast<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);
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) {
@ -492,15 +536,13 @@ bool NimBLEDevice::setPower(int8_t dbm, NimBLETxPowerType type) {
# 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
@ -536,7 +578,7 @@ int NimBLEDevice::getPower(NimBLETxPowerType type) {
# endif
# else
(void)type; // unused
return ble_phy_txpwr_get();
return ble_phy_tx_power_get();
# endif
} // getPower
@ -571,7 +613,7 @@ uint16_t NimBLEDevice::getMTU() {
/* BOND MANAGEMENT */
/* -------------------------------------------------------------------------- */
# if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL) || defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
# if CONFIG_BT_NIMBLE_ROLE_CENTRAL || CONFIG_BT_NIMBLE_ROLE_PERIPHERAL
/**
* @brief Gets the number of bonded peers stored
*/
@ -736,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:
@ -759,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.
@ -810,13 +850,13 @@ void NimBLEDevice::onSync(void) {
m_synced = true;
if (m_initialized) {
# if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
# if CONFIG_BT_NIMBLE_ROLE_OBSERVER
if (m_pScan != nullptr) {
m_pScan->onHostSync();
}
# endif
# if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
# if CONFIG_BT_NIMBLE_ROLE_BROADCASTER
if (m_bleAdvertising != nullptr) {
m_bleAdvertising->onHostSync();
}
@ -841,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
@ -863,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;
# elif defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S3)
bt_cfg.ble_max_act = CONFIG_BT_NIMBLE_MAX_CONNECTIONS;
# else
bt_cfg.nimble_max_connections = CONFIG_BT_NIMBLE_MAX_CONNECTIONS;
# endif
# ifdef CONFIG_BTDM_BLE_SCAN_DUPL
# 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) {
@ -882,17 +930,38 @@ 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
nimble_port_init();
@ -900,13 +969,15 @@ bool NimBLEDevice::init(const std::string& deviceName) {
// Setup callbacks for host events
ble_hs_cfg.reset_cb = NimBLEDevice::onReset;
ble_hs_cfg.sync_cb = NimBLEDevice::onSync;
ble_hs_cfg.store_status_cb = ble_store_util_status_rr; /*TODO: Implement handler for this*/
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_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)
@ -955,28 +1026,34 @@ bool NimBLEDevice::deinit(bool clearAll) {
}
if (clearAll) {
# if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
# if CONFIG_BT_NIMBLE_ROLE_PERIPHERAL
if (NimBLEDevice::m_pServer != nullptr) {
delete NimBLEDevice::m_pServer;
NimBLEDevice::m_pServer = nullptr;
}
# if CONFIG_BT_NIMBLE_L2CAP_COC_MAX_NUM
if (NimBLEDevice::m_pL2CAPServer != nullptr) {
delete NimBLEDevice::m_pL2CAPServer;
NimBLEDevice::m_pL2CAPServer = nullptr;
}
# endif
# endif
# if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
# if CONFIG_BT_NIMBLE_ROLE_BROADCASTER
if (NimBLEDevice::m_bleAdvertising != nullptr) {
delete NimBLEDevice::m_bleAdvertising;
NimBLEDevice::m_bleAdvertising = nullptr;
}
# endif
# if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
# if CONFIG_BT_NIMBLE_ROLE_OBSERVER
if (NimBLEDevice::m_pScan != nullptr) {
delete NimBLEDevice::m_pScan;
NimBLEDevice::m_pScan = nullptr;
}
# endif
# if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
# if CONFIG_BT_NIMBLE_ROLE_CENTRAL
for (auto clt : m_pClients) {
deleteClient(clt);
}
@ -1033,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
@ -1179,7 +1254,7 @@ bool NimBLEDevice::startSecurity(uint16_t connHandle, int* rcPtr) {
return rc == 0 || rc == BLE_HS_EALREADY;
} // startSecurity
# if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL) || defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
# if CONFIG_BT_NIMBLE_ROLE_CENTRAL || CONFIG_BT_NIMBLE_ROLE_PERIPHERAL
/**
* @brief Inject the provided passkey into the Security Manager.
* @param [in] peerInfo Connection information for the peer.
@ -1262,4 +1337,13 @@ void nimble_cpp_assert(const char* file, unsigned line) {
}
# endif // CONFIG_NIMBLE_CPP_DEBUG_ASSERT_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_ENABLED

View File

@ -1,5 +1,5 @@
/*
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -19,7 +19,7 @@
#define NIMBLE_CPP_DEVICE_H_
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED)
#if CONFIG_BT_ENABLED
# ifdef ESP_PLATFORM
# ifndef CONFIG_IDF_TARGET_ESP32P4
# include <esp_bt.h>
@ -40,16 +40,16 @@
# include <string>
# include <vector>
# if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
# if CONFIG_BT_NIMBLE_ROLE_CENTRAL
# include <array>
class NimBLEClient;
# endif
# if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
# if CONFIG_BT_NIMBLE_ROLE_OBSERVER
class NimBLEScan;
# endif
# if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
# if CONFIG_BT_NIMBLE_ROLE_BROADCASTER
# if CONFIG_BT_NIMBLE_EXT_ADV
class NimBLEExtAdvertising;
# else
@ -57,15 +57,19 @@ class NimBLEAdvertising;
# endif
# endif
# if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
# if CONFIG_BT_NIMBLE_ROLE_PERIPHERAL
class NimBLEServer;
# if CONFIG_BT_NIMBLE_L2CAP_COC_MAX_NUM > 0
class NimBLEL2CAPServer;
# endif
# endif
# if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) || defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
# if CONFIG_BT_NIMBLE_ROLE_PERIPHERAL || CONFIG_BT_NIMBLE_ROLE_CENTRAL
class NimBLEConnInfo;
# endif
class NimBLEAddress;
class NimBLEDeviceCallbacks;
# define BLEDevice NimBLEDevice
# define BLEClient NimBLEClient
@ -94,6 +98,13 @@ 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
@ -101,12 +112,7 @@ class NimBLEAddress;
# define NIMBLE_MAX_CONNECTIONS CONFIG_NIMBLE_MAX_CONNECTIONS
# endif
enum class NimBLETxPowerType {
All = 0,
Advertise = 1,
Scan = 2,
Connection = 3
};
enum class NimBLETxPowerType { All = 0, Advertise = 1, Scan = 2, Connection = 3 };
typedef int (*gap_event_handler)(ble_gap_event* event, void* arg);
@ -129,8 +135,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);
@ -147,6 +155,7 @@ class NimBLEDevice {
static void host_task(void* param);
static int getPower(NimBLETxPowerType type = NimBLETxPowerType::All);
static bool setPower(int8_t dbm, NimBLETxPowerType type = NimBLETxPowerType::All);
static bool setDefaultPhy(uint8_t txPhyMask, uint8_t rxPhyMask);
# ifdef ESP_PLATFORM
# ifndef CONFIG_IDF_TARGET_ESP32P4
@ -155,25 +164,25 @@ 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 CONFIG_BT_NIMBLE_ROLE_OBSERVER
static NimBLEScan* getScan();
# endif
# if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
# if CONFIG_BT_NIMBLE_ROLE_PERIPHERAL
static NimBLEServer* createServer();
static NimBLEServer* getServer();
# if CONFIG_BT_NIMBLE_L2CAP_COC_MAX_NUM > 0
static NimBLEL2CAPServer* createL2CAPServer();
static NimBLEL2CAPServer* getL2CAPServer();
# endif
# endif
# if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) || defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
# if CONFIG_BT_NIMBLE_ROLE_PERIPHERAL || CONFIG_BT_NIMBLE_ROLE_CENTRAL
static bool injectConfirmPasskey(const NimBLEConnInfo& peerInfo, bool accept);
static bool injectPassKey(const NimBLEConnInfo& peerInfo, uint32_t pin);
# endif
# if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
# if CONFIG_BT_NIMBLE_ROLE_BROADCASTER
# if CONFIG_BT_NIMBLE_EXT_ADV
static NimBLEExtAdvertising* getAdvertising();
static bool startAdvertising(uint8_t instId, int duration = 0, int maxEvents = 0);
@ -187,7 +196,7 @@ class NimBLEDevice {
# endif
# endif
# if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
# if CONFIG_BT_NIMBLE_ROLE_CENTRAL
static NimBLEClient* createClient();
static NimBLEClient* createClient(const NimBLEAddress& peerAddress);
static bool deleteClient(NimBLEClient* pClient);
@ -198,7 +207,7 @@ class NimBLEDevice {
static std::vector<NimBLEClient*> getConnectedClients();
# endif
# if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL) || defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
# if CONFIG_BT_NIMBLE_ROLE_CENTRAL || CONFIG_BT_NIMBLE_ROLE_PERIPHERAL
static bool deleteBond(const NimBLEAddress& address);
static int getNumBonds();
static bool isBonded(const NimBLEAddress& address);
@ -213,16 +222,21 @@ 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 CONFIG_BT_NIMBLE_ROLE_OBSERVER
static NimBLEScan* m_pScan;
# endif
# if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
# if CONFIG_BT_NIMBLE_ROLE_PERIPHERAL
static NimBLEServer* m_pServer;
# if CONFIG_BT_NIMBLE_L2CAP_COC_MAX_NUM > 0
static NimBLEL2CAPServer* m_pL2CAPServer;
# endif
# endif
# if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
# if CONFIG_BT_NIMBLE_ROLE_BROADCASTER
# if CONFIG_BT_NIMBLE_EXT_ADV
static NimBLEExtAdvertising* m_bleAdvertising;
# else
@ -230,31 +244,32 @@ class NimBLEDevice {
# endif
# endif
# if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
# if CONFIG_BT_NIMBLE_ROLE_CENTRAL
static std::array<NimBLEClient*, NIMBLE_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 CONFIG_BT_NIMBLE_ROLE_CENTRAL
friend class NimBLEClient;
# endif
# if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
# if CONFIG_BT_NIMBLE_ROLE_OBSERVER
friend class NimBLEScan;
# endif
# if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
# if CONFIG_BT_NIMBLE_ROLE_PERIPHERAL
friend class NimBLEServer;
friend class NimBLECharacteristic;
# endif
# if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
# if CONFIG_BT_NIMBLE_ROLE_BROADCASTER
friend class NimBLEAdvertising;
# if CONFIG_BT_NIMBLE_EXT_ADV
friend class NimBLEExtAdvertising;
@ -263,25 +278,29 @@ class NimBLEDevice {
# endif
};
# if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
# if CONFIG_BT_NIMBLE_ROLE_CENTRAL
# include "NimBLEClient.h"
# include "NimBLERemoteService.h"
# include "NimBLERemoteCharacteristic.h"
# include "NimBLERemoteDescriptor.h"
# endif
# if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
# if CONFIG_BT_NIMBLE_ROLE_OBSERVER
# include "NimBLEScan.h"
# endif
# if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
# if CONFIG_BT_NIMBLE_ROLE_PERIPHERAL
# include "NimBLEServer.h"
# include "NimBLEService.h"
# include "NimBLECharacteristic.h"
# include "NimBLEDescriptor.h"
# if CONFIG_BT_NIMBLE_L2CAP_COC_MAX_NUM
# include "NimBLEL2CAPServer.h"
# include "NimBLEL2CAPChannel.h"
# endif
# endif
# if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
# if CONFIG_BT_NIMBLE_ROLE_BROADCASTER
# if CONFIG_BT_NIMBLE_EXT_ADV
# include "NimBLEExtAdvertising.h"
# else
@ -289,11 +308,34 @@ class NimBLEDevice {
# endif
# endif
# if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL) || defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
# if CONFIG_BT_NIMBLE_ROLE_CENTRAL || CONFIG_BT_NIMBLE_ROLE_PERIPHERAL
# include "NimBLEConnInfo.h"
# endif
# include "NimBLEAddress.h"
# include "NimBLEUtils.h"
/**
* @brief Callbacks associated with a BLE device.
*/
class NimBLEDeviceCallbacks {
public:
virtual ~NimBLEDeviceCallbacks() {};
/**
* @brief Indicates an inability to perform a store operation.
* This callback should do one of two things:
* -Address the problem and return 0, indicating that the store operation
* should proceed.
* -Return nonzero to indicate that the store operation should be aborted.
* @param event Describes the store event being reported.
* BLE_STORE_EVENT_FULL; or
* BLE_STORE_EVENT_OVERFLOW
* @return 0 if the store operation should proceed;
* nonzero if the store operation should be aborted.
*/
virtual int onStoreStatus(struct ble_store_status_event* event, void* arg);
};
#endif // CONFIG_BT_ENABLED
#endif // NIMBLE_CPP_DEVICE_H_

View File

@ -1,5 +1,5 @@
/*
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -15,10 +15,9 @@
* limitations under the License.
*/
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
#include "NimBLEEddystoneTLM.h"
#if CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_BROADCASTER
# include "NimBLEEddystoneTLM.h"
# include "NimBLEUUID.h"
# include "NimBLELog.h"

View File

@ -1,5 +1,5 @@
/*
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -19,7 +19,7 @@
#define NIMBLE_CPP_EDDYSTONETLM_H_
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
#if CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_BROADCASTER
class NimBLEUUID;

View File

@ -1,5 +1,5 @@
/*
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -15,8 +15,8 @@
* 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_ENABLED && CONFIG_BT_NIMBLE_ROLE_BROADCASTER && CONFIG_BT_NIMBLE_EXT_ADV
# if defined(CONFIG_NIMBLE_CPP_IDF)
# include "services/gap/ble_svc_gap.h"
@ -24,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"
@ -69,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 CONFIG_BT_NIMBLE_ROLE_PERIPHERAL
NimBLEServer* pServer = NimBLEDevice::getServer();
if (pServer != nullptr) {
pServer->start(); // make sure the GATT server is ready before advertising
@ -392,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 CONFIG_BT_NIMBLE_ROLE_PERIPHERAL
m_params.connectable = enable;
# endif
} // setConnectable
@ -1091,4 +1090,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_ENABLED && CONFIG_BT_NIMBLE_ROLE_BROADCASTER && CONFIG_BT_NIMBLE_EXT_ADV

View File

@ -1,5 +1,5 @@
/*
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -19,7 +19,7 @@
#define NIMBLE_CPP_EXTADVERTISING_H_
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER) && CONFIG_BT_NIMBLE_EXT_ADV
#if CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_BROADCASTER && CONFIG_BT_NIMBLE_EXT_ADV
# if defined(CONFIG_NIMBLE_CPP_IDF)
# include "host/ble_gap.h"

View File

@ -1,5 +1,5 @@
/*
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -15,10 +15,9 @@
* limitations under the License.
*/
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#include "NimBLEHIDDevice.h"
#if CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_BROADCASTER && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL
# include "NimBLEHIDDevice.h"
# include "NimBLEServer.h"
# include "NimBLEService.h"
# include "NimBLE2904.h"
@ -341,4 +340,4 @@ NimBLEService* NimBLEHIDDevice::getBatteryService() {
return m_batterySvc;
} // getBatteryService
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL */
#endif // CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL

View File

@ -1,5 +1,5 @@
/*
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -19,7 +19,7 @@
#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)
#if CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_BROADCASTER && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL
# include <stdint.h>
# include <string>
@ -85,5 +85,5 @@ class NimBLEHIDDevice {
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_ENABLED && CONFIG_BT_NIMBLE_ROLE_BROADCASTER && CONFIG_BT_NIMBLE_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_ENABLED && CONFIG_BT_NIMBLE_L2CAP_COC_MAX_NUM
# include "NimBLEClient.h"
# include "NimBLELog.h"
# include "NimBLEUtils.h"
# if defined(CONFIG_NIMBLE_CPP_IDF)
# include "host/ble_gap.h"
# else
# include "nimble/nimble/host/include/host/ble_gap.h"
# endif
// L2CAP buffer block size
# define L2CAP_BUF_BLOCK_SIZE (250)
# define L2CAP_BUF_SIZE_MTUS_PER_CHANNEL (3)
// Round-up integer division
# define CEIL_DIVIDE(a, b) (((a) + (b) - 1) / (b))
# define ROUND_DIVIDE(a, b) (((a) + (b) / 2) / (b))
// Retry
constexpr uint32_t RetryTimeout = 50;
constexpr int RetryCounter = 3;
NimBLEL2CAPChannel::NimBLEL2CAPChannel(uint16_t psm, uint16_t mtu, NimBLEL2CAPChannelCallbacks* callbacks)
: psm(psm), mtu(mtu), callbacks(callbacks) {
assert(mtu); // fail here, if MTU is too little
assert(callbacks); // fail here, if no callbacks are given
assert(setupMemPool()); // fail here, if the memory pool could not be setup
NIMBLE_LOGI(LOG_TAG, "L2CAP COC 0x%04X initialized w/ L2CAP MTU %i", this->psm, this->mtu);
};
NimBLEL2CAPChannel::~NimBLEL2CAPChannel() {
teardownMemPool();
NIMBLE_LOGI(LOG_TAG, "L2CAP COC 0x%04X shutdown and freed.", this->psm);
}
bool NimBLEL2CAPChannel::setupMemPool() {
const size_t buf_blocks = CEIL_DIVIDE(mtu, L2CAP_BUF_BLOCK_SIZE) * L2CAP_BUF_SIZE_MTUS_PER_CHANNEL;
NIMBLE_LOGD(LOG_TAG, "Computed number of buf_blocks = %d", buf_blocks);
_coc_memory = malloc(OS_MEMPOOL_SIZE(buf_blocks, L2CAP_BUF_BLOCK_SIZE) * sizeof(os_membuf_t));
if (_coc_memory == 0) {
NIMBLE_LOGE(LOG_TAG, "Can't allocate _coc_memory: %d", errno);
return false;
}
auto rc = os_mempool_init(&_coc_mempool, buf_blocks, L2CAP_BUF_BLOCK_SIZE, _coc_memory, "appbuf");
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "Can't os_mempool_init: %d", rc);
return false;
}
auto rc2 = os_mbuf_pool_init(&_coc_mbuf_pool, &_coc_mempool, L2CAP_BUF_BLOCK_SIZE, buf_blocks);
if (rc2 != 0) {
NIMBLE_LOGE(LOG_TAG, "Can't os_mbuf_pool_init: %d", rc);
return false;
}
this->receiveBuffer = (uint8_t*)malloc(mtu);
if (!this->receiveBuffer) {
NIMBLE_LOGE(LOG_TAG, "Can't malloc receive buffer: %d, %s", errno, strerror(errno));
return false;
}
return true;
}
void NimBLEL2CAPChannel::teardownMemPool() {
if (this->callbacks) {
delete this->callbacks;
}
if (this->receiveBuffer) {
free(this->receiveBuffer);
}
if (_coc_memory) {
free(_coc_memory);
}
}
int NimBLEL2CAPChannel::writeFragment(std::vector<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 CONFIG_BT_NIMBLE_ROLE_CENTRAL
NimBLEL2CAPChannel* NimBLEL2CAPChannel::connect(NimBLEClient* client,
uint16_t psm,
uint16_t mtu,
NimBLEL2CAPChannelCallbacks* callbacks) {
if (!client->isConnected()) {
NIMBLE_LOGE(
LOG_TAG,
"Client is not connected. Before connecting via L2CAP, a GAP connection must have been established");
return nullptr;
};
auto channel = new NimBLEL2CAPChannel(psm, mtu, callbacks);
auto sdu_rx = os_mbuf_get_pkthdr(&channel->_coc_mbuf_pool, 0);
if (!sdu_rx) {
NIMBLE_LOGE(LOG_TAG, "Can't allocate SDU buffer: %d, %s", errno, strerror(errno));
return nullptr;
}
auto rc = ble_l2cap_connect(client->getConnHandle(), psm, mtu, sdu_rx, NimBLEL2CAPChannel::handleL2capEvent, channel);
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "ble_l2cap_connect failed: %d", rc);
}
return channel;
}
# endif // CONFIG_BT_NIMBLE_ROLE_CENTRAL
bool NimBLEL2CAPChannel::write(const std::vector<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_ENABLED && CONFIG_BT_NIMBLE_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 "nimconfig.h"
#if CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_L2CAP_COC_MAX_NUM
# include "inttypes.h"
# if defined(CONFIG_NIMBLE_CPP_IDF)
# include "host/ble_l2cap.h"
# include "os/os_mbuf.h"
# else
# include "nimble/nimble/host/include/host/ble_l2cap.h"
# include "nimble/porting/nimble/include/os/os_mbuf.h"
# endif
/**** FIX COMPILATION ****/
# undef min
# undef max
/**************************/
# include <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_ENABLED && CONFIG_BT_NIMBLE_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_ENABLED && CONFIG_BT_NIMBLE_L2CAP_COC_MAX_NUM
# include "NimBLEL2CAPChannel.h"
# include "NimBLEDevice.h"
# include "NimBLELog.h"
static const char* LOG_TAG = "NimBLEL2CAPServer";
NimBLEL2CAPServer::NimBLEL2CAPServer() {
// Nothing to do here...
}
NimBLEL2CAPServer::~NimBLEL2CAPServer() {
// Delete all services
for (auto service : this->services) {
delete service;
}
}
NimBLEL2CAPChannel* NimBLEL2CAPServer::createService(const uint16_t psm,
const uint16_t mtu,
NimBLEL2CAPChannelCallbacks* callbacks) {
auto service = new NimBLEL2CAPChannel(psm, mtu, callbacks);
auto rc = ble_l2cap_create_server(psm, mtu, NimBLEL2CAPChannel::handleL2capEvent, service);
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "Could not ble_l2cap_create_server: %d", rc);
return nullptr;
}
this->services.push_back(service);
return service;
}
#endif // CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_L2CAP_COC_MAX_NUM

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 "nimconfig.h"
#if CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_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_ENABLED && CONFIG_BT_NIMBLE_L2CAP_COC_MAX_NUM
#endif // NIMBLE_CPP_L2CAPSERVER_H_

View File

@ -1,5 +1,5 @@
/*
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -19,7 +19,7 @@
#define NIMBLE_CPP_LOCAL_ATTRIBUTE_H_
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#if CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL
# include "NimBLEAttribute.h"

View File

@ -1,5 +1,5 @@
/*
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -19,7 +19,7 @@
#define NIMBLE_LOCAL_VALUE_ATTRIBUTE_H_
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#if CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL
# if defined(CONFIG_NIMBLE_CPP_IDF)
# include "host/ble_hs.h"
@ -48,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.
@ -100,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;
@ -127,8 +102,7 @@ class NimBLELocalValueAttribute : public NimBLELocalAttribute {
uint16_t handle,
uint16_t maxLen,
uint16_t initLen = CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH)
: NimBLELocalAttribute(uuid, handle), m_value(initLen, maxLen) {}
: NimBLELocalAttribute(uuid, handle), NimBLEValueAttribute(maxLen, initLen) {}
/**
* @brief Destroy the NimBLELocalValueAttribute object.
*/
@ -163,8 +137,7 @@ 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

View File

@ -1,5 +1,5 @@
/*
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -19,7 +19,7 @@
#define NIMBLE_CPP_LOG_H_
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED)
#if CONFIG_BT_ENABLED
# if defined(CONFIG_NIMBLE_CPP_IDF)
# include "esp_log.h"
@ -110,16 +110,16 @@
# 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__); } \
# 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 { \
# define NIMBLE_CPP_LOG_PRINT(level, tag, format, ...) \
do { \
if (CONFIG_NIMBLE_CPP_LOG_LEVEL >= level) NIMBLE_CPP_LOG_LEVEL_LOCAL(level, tag, format, ##__VA_ARGS__); \
} while (0)
@ -172,5 +172,12 @@
# endif
# endif /* CONFIG_NIMBLE_CPP_IDF */
# 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_ENABLED */
#endif /* NIMBLE_CPP_LOG_H_ */

View File

@ -1,5 +1,5 @@
/*
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -15,10 +15,9 @@
* limitations under the License.
*/
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
#include "NimBLERemoteCharacteristic.h"
#if CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL
# include "NimBLERemoteCharacteristic.h"
# include "NimBLERemoteDescriptor.h"
# include "NimBLERemoteService.h"
# include "NimBLEClient.h"
@ -27,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;
@ -63,66 +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 (pChr->getHandle() != chr_val_handle) {
return 0; // 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));
return 0;
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
/**
@ -132,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
/**
@ -305,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);
};
/**
@ -393,4 +387,4 @@ NimBLEClient* NimBLERemoteCharacteristic::getClient() const {
return getRemoteService()->getClient();
} // getClient
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL */
#endif // CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL

View File

@ -1,5 +1,5 @@
/*
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -19,14 +19,16 @@
#define NIMBLE_CPP_REMOTE_CHARACTERISTIC_H_
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
#if CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL
# include "NimBLERemoteValueAttribute.h"
# include <vector>
# include <functional>
class NimBLEUUID;
class NimBLERemoteService;
class NimBLERemoteDescriptor;
struct NimBLEDescriptorFilter;
/**
* @brief A model of a remote BLE characteristic.
@ -65,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};

View File

@ -1,5 +1,5 @@
/*
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -15,10 +15,9 @@
* limitations under the License.
*/
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
#include "NimBLERemoteDescriptor.h"
#if CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL
# include "NimBLERemoteDescriptor.h"
# include "NimBLERemoteCharacteristic.h"
/**
@ -57,4 +56,4 @@ NimBLEClient* NimBLERemoteDescriptor::getClient() const {
return m_pRemoteCharacteristic->getClient();
}
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL */
#endif // CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL

View File

@ -1,5 +1,5 @@
/*
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -19,7 +19,7 @@
#define NIMBLE_CPP_REMOTE_DESCRIPTOR_H_
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
#if CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL
# include "NimBLERemoteValueAttribute.h"
@ -44,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_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL
#endif // NIMBLE_CPP_REMOTE_DESCRIPTOR_H_

View File

@ -1,5 +1,5 @@
/*
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -15,10 +15,9 @@
* limitations under the License.
*/
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
#include "NimBLERemoteService.h"
#if CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL
# include "NimBLERemoteService.h"
# include "NimBLERemoteCharacteristic.h"
# include "NimBLEClient.h"
# include "NimBLEAttValue.h"
@ -303,4 +302,4 @@ std::string NimBLERemoteService::toString() const {
return res;
} // toString
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL */
#endif // CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL

View File

@ -1,5 +1,5 @@
/*
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -19,7 +19,7 @@
#define NIMBLE_CPP_REMOTE_SERVICE_H_
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
#if CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL
# include "NimBLEAttribute.h"
# include <vector>
@ -64,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_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL
#endif // NIMBLE_CPP_REMOTE_SERVICE_H_

View File

@ -1,5 +1,5 @@
/*
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -15,10 +15,9 @@
* limitations under the License.
*/
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
#include "NimBLERemoteValueAttribute.h"
#if CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL
# include "NimBLERemoteValueAttribute.h"
# include "NimBLEClient.h"
# include "NimBLEUtils.h"
# include "NimBLELog.h"
@ -122,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{};

View File

@ -1,5 +1,5 @@
/*
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -19,7 +19,7 @@
#define NIMBLE_CPP_REMOTE_VALUE_ATTRIBUTE_H_
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
#if CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL
# if defined(CONFIG_NIMBLE_CPP_IDF)
# include <host/ble_gatt.h>
@ -32,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.
@ -153,20 +140,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.
@ -177,16 +150,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.
@ -195,9 +168,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_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL
#endif // NIMBLE_CPP_REMOTE_VALUE_ATTRIBUTE_H_

View File

@ -1,5 +1,5 @@
/*
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -15,10 +15,9 @@
* limitations under the License.
*/
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
#include "NimBLEScan.h"
#if CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_OBSERVER
# include "NimBLEScan.h"
# include "NimBLEDevice.h"
# include "NimBLELog.h"
@ -42,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;
}
}
/**
@ -57,6 +58,11 @@ int NimBLEScan::handleGapEvent(ble_gap_event* event, void* arg) {
switch (event->type) {
case BLE_GAP_EVENT_EXT_DISC:
case BLE_GAP_EVENT_DISC: {
if (!pScan->isScanning()) {
NIMBLE_LOGI(LOG_TAG, "Scan stopped, ignoring event");
return 0;
}
# if CONFIG_BT_NIMBLE_EXT_ADV
const auto& disc = event->ext_disc;
const bool isLegacyAdv = disc.props & BLE_HCI_ADV_LEGACY_MASK;
@ -68,7 +74,7 @@ int NimBLEScan::handleGapEvent(ble_gap_event* event, void* arg) {
# endif
NimBLEAddress advertisedAddress(disc.addr);
# ifdef CONFIG_BT_NIMBLE_ROLE_CENTRAL
# if CONFIG_BT_NIMBLE_ROLE_CENTRAL
// stop processing if already connected
NimBLEClient* pClient = NimBLEDevice::getClientByPeerAddress(advertisedAddress);
if (pClient != nullptr && pClient->isConnected()) {
@ -483,11 +489,11 @@ void NimBLEScan::clearResults() {
* @brief Dump the scan results to the log.
*/
void NimBLEScanResults::dump() const {
#if CONFIG_NIMBLE_CPP_LOG_LEVEL >=3
# if CONFIG_NIMBLE_CPP_LOG_LEVEL >= 3
for (const auto& dev : m_deviceVec) {
NIMBLE_LOGI(LOG_TAG, "- %s", dev->toString().c_str());
}
#endif
# endif
} // dump
/**
@ -554,4 +560,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_ENABLED && CONFIG_BT_NIMBLE_ROLE_OBSERVER

View File

@ -1,5 +1,5 @@
/*
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -19,7 +19,7 @@
#define NIMBLE_CPP_SCAN_H_
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
#if CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_OBSERVER
# include "NimBLEAdvertisedDevice.h"
# include "NimBLEUtils.h"

View File

@ -1,5 +1,5 @@
/*
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -15,14 +15,13 @@
* limitations under the License.
*/
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#include "NimBLEServer.h"
#if CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL
# include "NimBLEServer.h"
# include "NimBLEDevice.h"
# include "NimBLELog.h"
# if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
# if CONFIG_BT_NIMBLE_ROLE_CENTRAL
# include "NimBLEClient.h"
# endif
@ -70,7 +69,7 @@ NimBLEServer::~NimBLEServer() {
delete m_pServerCallbacks;
}
# if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
# if CONFIG_BT_NIMBLE_ROLE_CENTRAL
if (m_pClient != nullptr) {
delete m_pClient;
}
@ -154,7 +153,7 @@ NimBLEExtAdvertising* NimBLEServer::getAdvertising() const {
} // getAdvertising
# endif
# if !CONFIG_BT_NIMBLE_EXT_ADV || defined(_DOXYGEN_)
# if (!CONFIG_BT_NIMBLE_EXT_ADV && CONFIG_BT_NIMBLE_ROLE_BROADCASTER) || defined(_DOXYGEN_)
/**
* @brief Retrieve the advertising object that can be used to advertise the existence of the server.
* @return A pointer to an advertising object.
@ -355,9 +354,16 @@ 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 !CONFIG_BT_NIMBLE_EXT_ADV && CONFIG_BT_NIMBLE_ROLE_BROADCASTER
NimBLEDevice::startAdvertising();
# endif
} else {
@ -401,7 +407,7 @@ int NimBLEServer::handleGapEvent(ble_gap_event* event, void* arg) {
}
}
# if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
# if CONFIG_BT_NIMBLE_ROLE_CENTRAL
if (pServer->m_pClient && pServer->m_pClient->m_connHandle == event->disconnect.conn.conn_handle) {
// If this was also the client make sure it's flagged as disconnected.
pServer->m_pClient->m_connHandle = BLE_HS_CONN_HANDLE_NONE;
@ -445,7 +451,7 @@ int NimBLEServer::handleGapEvent(ble_gap_event* event, void* arg) {
chr->m_pCallbacks->onSubscribe(chr,
peerInfo,
event->subscribe.cur_notify + event->subscribe.cur_indicate);
event->subscribe.cur_notify + (event->subscribe.cur_indicate << 1));
}
}
}
@ -488,10 +494,10 @@ int NimBLEServer::handleGapEvent(ble_gap_event* event, void* arg) {
} // BLE_GAP_EVENT_NOTIFY_TX
case BLE_GAP_EVENT_ADV_COMPLETE: {
# if CONFIG_BT_NIMBLE_EXT_ADV
# if CONFIG_BT_NIMBLE_EXT_ADV && CONFIG_BT_NIMBLE_ROLE_BROADCASTER
case BLE_GAP_EVENT_SCAN_REQ_RCVD:
return NimBLEExtAdvertising::handleGapEvent(event, arg);
# else
# elif CONFIG_BT_NIMBLE_ROLE_BROADCASTER
return NimBLEAdvertising::handleGapEvent(event, arg);
# endif
} // BLE_GAP_EVENT_ADV_COMPLETE | BLE_GAP_EVENT_SCAN_REQ_RCVD
@ -531,6 +537,11 @@ int NimBLEServer::handleGapEvent(ble_gap_event* event, void* arg) {
}
pServer->m_pServerCallbacks->onAuthenticationComplete(peerInfo);
# if CONFIG_BT_NIMBLE_ROLE_CENTRAL
if (pServer->m_pClient && pServer->m_pClient->m_connHandle == event->enc_change.conn_handle) {
NimBLEClient::handleGapEvent(event, pServer->m_pClient);
}
# endif
break;
} // BLE_GAP_EVENT_ENC_CHANGE
@ -544,7 +555,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) {
@ -554,7 +564,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};
@ -620,13 +629,10 @@ int NimBLEServer::handleGattEvent(uint16_t connHandle, uint16_t attrHandle, ble_
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 || val.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();
@ -638,12 +644,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;
@ -731,7 +737,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 !CONFIG_BT_NIMBLE_EXT_ADV && CONFIG_BT_NIMBLE_ROLE_BROADCASTER
NimBLEDevice::getAdvertising()->removeServiceUUID(service->getUUID());
# endif
} // removeService
@ -768,7 +774,9 @@ void NimBLEServer::resetGATT() {
return;
}
# if CONFIG_BT_NIMBLE_ROLE_BROADCASTER
NimBLEDevice::stopAdvertising();
# endif
ble_gatts_reset();
ble_svc_gap_init();
ble_svc_gatt_init();
@ -791,29 +799,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.
@ -857,9 +842,33 @@ bool NimBLEServer::getPhy(uint16_t connHandle, uint8_t* txPhy, uint8_t* rxPhy) {
return rc == 0;
} // getPhy
# 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
# endif
# if !CONFIG_BT_NIMBLE_EXT_ADV || defined(_DOXYGEN_)
# if (!CONFIG_BT_NIMBLE_EXT_ADV && CONFIG_BT_NIMBLE_ROLE_BROADCASTER) || defined(_DOXYGEN_)
/**
* @brief Start advertising.
* @param [in] duration The duration in milliseconds to advertise for, default = forever.
@ -936,7 +945,7 @@ void NimBLEServer::setDataLen(uint16_t connHandle, uint16_t octets) const {
# endif
} // setDataLen
# if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
# if CONFIG_BT_NIMBLE_ROLE_CENTRAL
/**
* @brief Create a client instance from the connection handle.
* @param [in] connHandle The connection handle to create a client instance from.
@ -1019,10 +1028,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_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL

View File

@ -1,5 +1,5 @@
/*
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -19,7 +19,7 @@
#define NIMBLE_CPP_SERVER_H_
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#if CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL
# if defined(CONFIG_NIMBLE_CPP_IDF)
# include "host/ble_gap.h"
@ -45,12 +45,14 @@ class NimBLEConnInfo;
class NimBLEAddress;
class NimBLEService;
class NimBLECharacteristic;
# if CONFIG_BT_NIMBLE_EXT_ADV
# if CONFIG_BT_NIMBLE_ROLE_BROADCASTER
# if CONFIG_BT_NIMBLE_EXT_ADV
class NimBLEExtAdvertising;
# else
# else
class NimBLEAdvertising;
# endif
# endif
# if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
# if CONFIG_BT_NIMBLE_ROLE_CENTRAL
class NimBLEClient;
# endif
@ -79,35 +81,39 @@ 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 CONFIG_BT_NIMBLE_ROLE_CENTRAL
NimBLEClient* getClient(uint16_t connHandle);
NimBLEClient* getClient(const NimBLEConnInfo& connInfo);
void deleteClient();
# endif
# if CONFIG_BT_NIMBLE_EXT_ADV
# if CONFIG_BT_NIMBLE_ROLE_BROADCASTER
# if CONFIG_BT_NIMBLE_EXT_ADV
NimBLEExtAdvertising* getAdvertising() const;
bool startAdvertising(uint8_t instanceId, int duration = 0, int maxEvents = 0) const;
bool stopAdvertising(uint8_t instanceId) const;
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 !CONFIG_BT_NIMBLE_EXT_ADV || defined(_DOXYGEN_)
NimBLEAdvertising* getAdvertising() const;
bool startAdvertising(uint32_t duration = 0) const;
bool stopAdvertising() const;
# endif
# endif
private:
friend class NimBLEDevice;
friend class NimBLEService;
friend class NimBLECharacteristic;
# if CONFIG_BT_NIMBLE_EXT_ADV
# if CONFIG_BT_NIMBLE_ROLE_BROADCASTER
# if CONFIG_BT_NIMBLE_EXT_ADV
friend class NimBLEExtAdvertising;
# else
# else
friend class NimBLEAdvertising;
# endif
# endif
NimBLEServer();
@ -123,7 +129,7 @@ class NimBLEServer {
std::vector<NimBLEService*> m_svcVec;
std::array<uint16_t, CONFIG_BT_NIMBLE_MAX_CONNECTIONS> m_connectedPeers;
# if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
# if CONFIG_BT_NIMBLE_ROLE_CENTRAL
NimBLEClient* m_pClient{nullptr};
# endif
@ -203,7 +209,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
@ -216,7 +221,6 @@ 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

View File

@ -1,5 +1,5 @@
/*
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -15,10 +15,9 @@
* limitations under the License.
*/
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#include "NimBLEService.h"
#if CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL
# include "NimBLEService.h"
# if CONFIG_BT_NIMBLE_EXT_ADV
# include "NimBLEExtAdvertising.h"
# else
@ -376,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_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL

View File

@ -1,5 +1,5 @@
/*
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -19,7 +19,7 @@
#define NIMBLE_CPP_SERVICE_H_
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#if CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL
class NimBLEService;
@ -69,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_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL
#endif // NIMBLE_CPP_SERVICE_H_

View File

@ -1,5 +1,5 @@
/*
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -15,11 +15,10 @@
* limitations under the License.
*/
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include "NimBLEUUID.h"
#if CONFIG_BT_ENABLED
# include "NimBLEUtils.h"
# include "NimBLEUUID.h"
# include "NimBLELog.h"
/**** FIX COMPILATION ****/

View File

@ -1,5 +1,5 @@
/*
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -19,7 +19,7 @@
#define NIMBLE_CPP_UUID_H_
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED)
#if CONFIG_BT_ENABLED
# if defined(CONFIG_NIMBLE_CPP_IDF)
# include "host/ble_uuid.h"
@ -70,5 +70,5 @@ class NimBLEUUID {
ble_uuid_any_t m_uuid{};
}; // NimBLEUUID
#endif /* CONFIG_BT_ENABLED */
#endif /* NIMBLE_CPP_UUID_H_ */
#endif // CONFIG_BT_ENABLED
#endif // NIMBLE_CPP_UUID_H_

View File

@ -1,5 +1,5 @@
/*
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -15,10 +15,9 @@
* limitations under the License.
*/
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include "NimBLEUtils.h"
#if CONFIG_BT_ENABLED
# include "NimBLEUtils.h"
# include "NimBLEAddress.h"
# include "NimBLELog.h"
@ -511,22 +510,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

View File

@ -1,5 +1,5 @@
/*
* Copyright 2020-2024 Ryan Powell <ryan@nable-embedded.io> and
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -19,7 +19,8 @@
#define NIMBLE_CPP_UTILS_H_
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED)
#if CONFIG_BT_ENABLED
# include <string>
class NimBLEAddress;

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 "nimconfig.h"
#if CONFIG_BT_ENABLED && (CONFIG_BT_NIMBLE_ROLE_PERIPHERAL || CONFIG_BT_NIMBLE_ROLE_CENTRAL)
# include "NimBLEAttribute.h"
# include "NimBLEAttValue.h"
class NimBLEValueAttribute {
public:
NimBLEValueAttribute(uint16_t maxLen = BLE_ATT_ATTR_MAX_LEN, uint16_t initLen = CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH)
: m_value(initLen, maxLen) {}
/**
* @brief Get a copy of the value of the attribute value.
* @param [in] timestamp (Optional) A pointer to a time_t struct to get the time the value set.
* @return A copy of the attribute value.
*/
NimBLEAttValue getValue(time_t* timestamp) const { return m_value.getValue(timestamp); }
/**
* @brief Get a copy of the value of the attribute value.
* @return A copy of the attribute value.
*/
NimBLEAttValue getValue() const { return m_value; }
/**
* @brief Get the length of the attribute value.
* @return The length of the attribute value.
*/
size_t getLength() const { return m_value.size(); }
/**
* @brief Template to convert the data to <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_ENABLED && (CONFIG_BT_NIMBLE_ROLE_PERIPHERAL || CONFIG_BT_NIMBLE_ROLE_CENTRAL)
#endif // NIMBLE_CPP_ATTRIBUTE_H_

View File

@ -15,16 +15,6 @@
// Allows cpp wrapper to select the correct include paths when using esp-idf
#define CONFIG_NIMBLE_CPP_IDF
/* Cannot use client without scan */
#if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL) && !defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
#define CONFIG_BT_NIMBLE_ROLE_OBSERVER
#endif
/* Cannot use server without advertise */
#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) && !defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
#define CONFIG_BT_NIMBLE_ROLE_BROADCASTER
#endif
/* Enables the use of Arduino String class for attribute values */
#if defined __has_include
# if __has_include (<Arduino.h>)

View File

@ -8,19 +8,19 @@
#define CONFIG_BT_NIMBLE_ENABLED
#endif
#if defined(CONFIG_NIMBLE_ROLE_OBSERVER) && !defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
#if defined(CONFIG_NIMBLE_ROLE_OBSERVER) && !CONFIG_BT_NIMBLE_ROLE_OBSERVER
#define CONFIG_BT_NIMBLE_ROLE_OBSERVER
#endif
#if defined(CONFIG_NIMBLE_ROLE_BROADCASTER) && !defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
#if defined(CONFIG_NIMBLE_ROLE_BROADCASTER) && !CONFIG_BT_NIMBLE_ROLE_BROADCASTER
#define CONFIG_BT_NIMBLE_ROLE_BROADCASTER
#endif
#if defined(CONFIG_NIMBLE_ROLE_CENTRAL) && !defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
#if defined(CONFIG_NIMBLE_ROLE_CENTRAL) && !CONFIG_BT_NIMBLE_ROLE_CENTRAL
#define CONFIG_BT_NIMBLE_ROLE_CENTRAL
#endif
#if defined(CONFIG_NIMBLE_ROLE_PERIPHERAL) && !defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#if defined(CONFIG_NIMBLE_ROLE_PERIPHERAL) && !CONFIG_BT_NIMBLE_ROLE_PERIPHERAL
#define CONFIG_BT_NIMBLE_ROLE_PERIPHERAL
#endif
@ -63,3 +63,31 @@
#if defined(CONFIG_BT_NIMBLE_EXT_ADV_MAX_SIZE) && !defined(CONFIG_BT_NIMBLE_MAX_EXT_ADV_DATA_LEN)
#define CONFIG_BT_NIMBLE_MAX_EXT_ADV_DATA_LEN CONFIG_BT_NIMBLE_EXT_ADV_MAX_SIZE
#endif
#if !defined(CONFIG_BTDM_BLE_SCAN_DUPL) && defined(CONFIG_BT_CTRL_BLE_SCAN_DUPL)
#define CONFIG_BTDM_BLE_SCAN_DUPL CONFIG_BT_CTRL_BLE_SCAN_DUPL
#endif
#if !defined(CONFIG_BTDM_SCAN_DUPL_TYPE_DEVICE) && defined(CONFIG_BT_CTRL_SCAN_DUPL_TYPE_DEVICE)
#define CONFIG_BTDM_SCAN_DUPL_TYPE_DEVICE CONFIG_BT_CTRL_SCAN_DUPL_TYPE_DEVICE
#endif
#if !defined(CONFIG_BTDM_SCAN_DUPL_TYPE_DATA) && defined(CONFIG_BT_CTRL_SCAN_DUPL_TYPE_DATA)
#define CONFIG_BTDM_SCAN_DUPL_TYPE_DATA CONFIG_BT_CTRL_SCAN_DUPL_TYPE_DATA
#endif
#if !defined(CONFIG_BTDM_SCAN_DUPL_TYPE_DATA_DEVICE) && defined(CONFIG_BT_CTRL_SCAN_DUPL_TYPE_DATA_DEVICE)
#define CONFIG_BTDM_SCAN_DUPL_TYPE_DATA_DEVICE CONFIG_BT_CTRL_SCAN_DUPL_TYPE_DATA_DEVICE
#endif
#ifdef CONFIG_BT_LE_LL_CFG_FEAT_LE_CODED_PHY
#define CONFIG_BT_NIMBLE_LL_CFG_FEAT_LE_CODED_PHY CONFIG_BT_LE_LL_CFG_FEAT_LE_CODED_PHY
#endif
#ifdef CONFIG_BT_LE_LL_CFG_FEAT_LE_2M_PHY
#define CONFIG_BT_NIMBLE_LL_CFG_FEAT_LE_2M_PHY CONFIG_BT_LE_LL_CFG_FEAT_LE_2M_PHY
#endif
#ifdef CONFIG_BT_LE_50_FEATURE_SUPPORT
#define CONFIG_BT_NIMBLE_50_FEATURE_SUPPORT CONFIG_BT_LE_50_FEATURE_SUPPORT
#endif