20 Commits

Author SHA1 Message Date
h2zero
f80605aff8 Release 2.3.2 2025-09-02 14:54:56 -06:00
h2zero
e26c7406fb Improve macros for code enablement 2025-09-02 14:42:46 -06:00
h2zero
a547f2529a Fix docs build 2025-09-02 14:42:42 -06:00
h2zero
ad145ad503 Fix build with idf v5.5+ and specific roles are defined. 2025-09-02 14:42:40 -06:00
h2zero
ecc617f9eb Refactor to use MYNEWT_VAL macros.
This replaces the previously prefixed CONFIG_BT_X config macros with the underlying MYNEWT_VAL_X config macros that they affected.
2025-09-02 14:42:38 -06:00
iranl
485a01b78c Fix undefined reference to ble_svc_gap_device_name_set when GATT server is disabled (#349)
* Fix undefined reference to ble_svc_gap_device_name_set when GATT server is disabled

* Do not affect ESP-IDF <5.5.0
2025-09-02 14:42:32 -06:00
h2zero
f0ca3bf35d [Bugfix] OnConnectfail not called when connection not established.
Workaround for when the disconnect event is sent when no connection has been established.
Espressif changed this from a connect event with error code to disconnect event.
2025-09-02 14:42:30 -06:00
h2zero
ac55482b18 Change default security settings to BLE secure connections off.
Fixing some connection issues when enabled, users should enable if desired.
2025-09-02 14:42:28 -06:00
h2zero
8b9e430e5b [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-09-02 14:42:26 -06:00
h2zero
6da9905235 Fix build with idf versions < 5.x 2025-09-02 14:42:23 -06:00
h2zero
18cc463c2c [Bugfix] allow peripheral and central roles without broadcast/scan. 2025-09-02 14:42:21 -06:00
h2zero
e7a1462a99 Bump idf_component version 2025-06-11 11:40:56 -06:00
h2zero
9e141c9f58 Fix library.json version 2025-06-11 11:34:26 -06:00
h2zero
e5dbd26693 Release 2.3.1 2025-06-11 11:16:49 -06:00
h2zero
2272e3c4a7 Update docs 2025-06-11 11:16:46 -06:00
Larry Davis
8c6a9d4258 Support passing data directly from NimBLEBeacon.getData() to NimBLEAdvertisementData.setManufacturerData() 2025-06-11 11:16:42 -06:00
h2zero
e4d202f1ce [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-11 11:16:38 -06:00
h2zero
20349d9e8b 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-11 11:16:35 -06:00
h2zero
96c142034e Fix builds when exluding roles 2025-06-11 11:16:22 -06:00
John Boiles
252b4f5644 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-06-11 11:16:16 -06:00
31 changed files with 193 additions and 1348 deletions

View File

@@ -45,7 +45,7 @@ jobs:
container: espressif/idf:${{ matrix.idf_ver }}
steps:
- name: Checkout
uses: actions/checkout@v5
uses: actions/checkout@v4
with:
path: components/esp-nimble-cpp
- name: Build examples

View File

@@ -8,7 +8,7 @@ jobs:
build_docs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v4
- name: Doxygen Action
uses: mattnotmitt/doxygen-action@v1.9.8
with:
@@ -17,27 +17,4 @@ jobs:
uses: peaceiris/actions-gh-pages@v4
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./docs/doxydocs/html
upload_to_component_registry:
permissions:
id-token: write
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
with:
submodules: "recursive"
- name: Upload component to the component registry
uses: espressif/upload-components-ci-action@v2
with:
components: "esp-nimble-cpp: ."
namespace: "h2zero"
upload_to_platformio_registry:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- name: Upload component to PlatformIO Registry
uses: bojit/platformio-publish@main
with:
token: ${{ secrets.PLATFORMIO_AUTH_TOKEN }}
publish_dir: ./docs/doxydocs/html

1
.gitignore vendored
View File

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

View File

@@ -1,15 +1,6 @@
# Changelog
All notable changes to this project will be documented in this file.
## [2.3.3] 2025-09-05
## Fixed
- NimBLEAdvertisedDevice::isConnectable returning incorrect result.
- Extended advertisements not reporting full data.
## Added
- Support up to 1650 bytes of advertisement with extended advertising
## [2.3.2] 2025-09-02
## Fixed

View File

@@ -9,8 +9,6 @@
NimBLE CPP library for use with ESP32 that attempts to maintain compatibility with the [nkolban cpp_utils BLE API](https://github.com/nkolban/esp32-snippets/tree/master/cpp_utils).
The library is threadsafe, characteristics can be set from any thread.
**An Arduino version of this library, including NimBLE, can be [found here.](https://github.com/h2zero/NimBLE-Arduino)**
This library **significantly** reduces resource usage and improves performance for ESP32 BLE applications as compared

View File

@@ -55,7 +55,7 @@ The default address type is public `0`, whereas many devices use a random `1` ad
If you experience this issue please create your address instances with the address type specified, i.e. `NimBLEAddress address("11:22:33:44:55:66", TYPE_HERE)`
`NimBLEAddress::getNative` has been removed and replaced with `NimBLEAddress::getBase`.
This returns a pointer to `const ble_addr_t` instead of a pointer to the address value that `getNative` did. The value can be accessed through this struct via `ble_addr_t.val` and type is in `ble_addr_t.type`.
This returns a pointer to `const ble_addr_t` instead of a pointer to the address value that `getNative` did. The value can be accessed through this struct via `ble_addr_t.value` and type is in `ble_addr_t.type`.
<br/>
## BLE UUID's

View File

@@ -48,7 +48,7 @@ PROJECT_NAME = esp-nimble-cpp
# could be handy for archiving the generated documentation or if some version
# control system is used.
PROJECT_NUMBER = 2.3.3
PROJECT_NUMBER = 2.3.2
# 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

@@ -1,8 +1,11 @@
# Usage Tips
## Threadsafety
## Put BLE functions in a task running on the NimBLE stack core
This library is threadsafe. Attribues can be manipulated freely.
When commands are sent to the stack from a different core they can experience delays in execution.
This library detects this and invokes the esp32 IPC to reroute these commands through the correct core but this also increases overhead.
Therefore it is highly recommended to create tasks for BLE to run on the same core, the macro `CONFIG_BT_NIMBLE_PINNED_TO_CORE` can be used to set the core.
<br/>
## Do not delete client instances unless necessary or unused
@@ -29,41 +32,6 @@ or nullptr such as when calling `NimBLEClient::getService`. The latter being a
Most of the functions in this library return something that should be checked before proceeding.
<br/>
## Persisted bonds can be lost due to low MAX_CCCDS
The symptom: CONFIG_BT_NIMBLE_MAX_BONDS is set to N, but a smaller number of bonds is preserved, perhaps even a single bond. The limitation of persisted bonds can be observed via NimBLEDevice::getNumBonds(). The number may not reach CONFIG_BT_NIMBLE_MAX_BONDS.
Cause: For each bond, NimBLE persists each of the CCCD (client characteristic configuration descriptor) values that have been subscribed
to by the client. If CONFIG_BT_NIMBLE_MAX_CCCDS is too low, the older CCCD values are overwritten by the newer ones. The loss of the older
CCCDs values results in those bonds being lost.
Fix: Increase CONFIG_BT_NIMBLE_MAX_CCCDS. These take approximately 40 bytes in NVS, 2 bytes for the CCCD value and the NVS metadata overhead. The value of
CONFIG_BT_NIMBLE_MAX_CCCDS should conservatively be no less than (CONFIG_BT_NIMBLE_MAX_BONDS * {maximum number of characteristics that can be subscribed to}).
## Device 'Local Name'
'Local name' refers to how the device is seen and displayed.
A devices 'Local name' can be thought of as coming from two places, the <i>Advertising "Local name"</i> and the <i>the GATT Device Name</i>.
### Advertising "Local name"
Field found in the advertising data payload. Value is set via NimBLEAdvertising::setName().
### GATT Device Name
Characteristic UUID 0x2A00 in the Generic Access service. Set via NimBLEDevice::init() or NimBLEDevice::setDeviceName().
This characteristic is read <b>after</b> connecting to the device.
### Important considerations
* OSes cache the <i>'GATT Device Name'</i>.
* OSes update the device name based on the <i>'GATT Device Name'</i> after connecting to a device. This means that if you set the <i>Advertising 'Local name'</i> to "ABCD" but the <i>'GATT Device Name'</i> to "12345", the device will be seen as "ABCD" until connecting to the device, at which time the devices name will change to "12345".
* If no <i>'Advertising "Local name"'</i> is set, OSes, such as iOS, may display the devices name as 'Unnamed' until the device is connected to, at which time the <i>'GATT Device Name'</i> is read and used instead.
It is recommended that both <i>'Advertising "Local name"'</i> <b>and</b> <i>'GATT Device Name'</i> be set appropriately, after considering the above described behavior.
## There will be bugs - please report them
No code is bug free and unit testing will not find them all on it's own. If you encounter a bug, please report it along with any logs and decoded backtrace if applicable.

View File

@@ -1,5 +1,5 @@
## IDF Component Manager Manifest File
version: "2.3.3"
version: "2.3.2"
license: "Apache-2.0"
description: "C++ wrapper for the NimBLE BLE stack"
url: "https://github.com/h2zero/esp-nimble-cpp"

View File

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

View File

@@ -22,29 +22,13 @@
# include <algorithm>
# ifndef MYNEWT_VAL_NIMBLE_CPP_ADDR_FMT_EXCLUDE_DELIMITER
# ifndef CONFIG_NIMBLE_CPP_ADDR_FMT_EXCLUDE_DELIMITER
# define MYNEWT_VAL_NIMBLE_CPP_ADDR_FMT_EXCLUDE_DELIMITER (0)
# else
# define MYNEWT_VAL_NIMBLE_CPP_ADDR_FMT_EXCLUDE_DELIMITER CONFIG_NIMBLE_CPP_ADDR_FMT_EXCLUDE_DELIMITER
# endif
# endif
# ifndef MYNEWT_VAL_NIMBLE_CPP_ADDR_FMT_UPPERCASE
# ifndef CONFIG_NIMBLE_CPP_ADDR_FMT_UPPERCASE
# define MYNEWT_VAL_NIMBLE_CPP_ADDR_FMT_UPPERCASE (1)
# else
# define MYNEWT_VAL_NIMBLE_CPP_ADDR_FMT_UPPERCASE CONFIG_NIMBLE_CPP_ADDR_FMT_UPPERCASE
# endif
# endif
# if MYNEWT_VAL(NIMBLE_CPP_ADDR_FMT_EXCLUDE_DELIMITER)
# ifdef CONFIG_NIMBLE_CPP_ADDR_FMT_EXCLUDE_DELIMITER
# define NIMBLE_CPP_ADDR_DELIMITER ""
# else
# define NIMBLE_CPP_ADDR_DELIMITER ":"
# endif
# if MYNEWT_VAL(NIMBLE_CPP_ADDR_FMT_UPPERCASE)
# ifdef CONFIG_NIMBLE_CPP_ADDR_FMT_UPPERCASE
# define NIMBLE_CPP_ADDR_FMT "%02X%s%02X%s%02X%s%02X%s%02X%s%02X"
# else
# define NIMBLE_CPP_ADDR_FMT "%02x%s%02x%s%02x%s%02x%s%02x%s%02x"

View File

@@ -38,7 +38,6 @@ NimBLEAdvertisedDevice::NimBLEAdvertisedDevice(const ble_gap_event* event, uint8
m_callbackSent{0},
m_advLength{event->ext_disc.length_data},
m_isLegacyAdv{!!(event->ext_disc.props & BLE_HCI_ADV_LEGACY_MASK)},
m_dataStatus{event->ext_disc.data_status},
m_sid{event->ext_disc.sid},
m_primPhy{event->ext_disc.prim_phy},
m_secPhy{event->ext_disc.sec_phy},
@@ -61,16 +60,7 @@ NimBLEAdvertisedDevice::NimBLEAdvertisedDevice(const ble_gap_event* event, uint8
void NimBLEAdvertisedDevice::update(const ble_gap_event* event, uint8_t eventType) {
# if MYNEWT_VAL(BLE_EXT_ADV)
const auto& disc = event->ext_disc;
if (m_dataStatus == BLE_GAP_EXT_ADV_DATA_STATUS_INCOMPLETE) {
m_payload.reserve(m_advLength + disc.length_data);
m_payload.insert(m_payload.end(), disc.data, disc.data + disc.length_data);
m_dataStatus = disc.data_status;
m_advLength = m_payload.size();
return;
}
m_dataStatus = disc.data_status;
m_isLegacyAdv = disc.props & BLE_HCI_ADV_LEGACY_MASK;
m_isLegacyAdv = disc.props & BLE_HCI_ADV_LEGACY_MASK;
# else
const auto& disc = event->disc;
# endif
@@ -96,11 +86,11 @@ const NimBLEAddress& NimBLEAdvertisedDevice::getAddress() const {
/**
* @brief Get the advertisement type.
* @return The advertising type the device is reporting:
* * BLE_HCI_ADV_RPT_EVTYPE_ADV_IND (0) - indirect advertising - connectable and scannable
* * BLE_HCI_ADV_RPT_EVTYPE_DIR_IND (1) - direct advertising - connectable
* * BLE_HCI_ADV_RPT_EVTYPE_SCAN_IND (2) - indirect scan response - not connectable - scannable
* * BLE_HCI_ADV_RPT_EVTYPE_NONCONN_IND (3) - beacon only - not connectable - not scannable
* * BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP (4) - scan response
* * BLE_HCI_ADV_TYPE_ADV_IND (0) - indirect advertising
* * BLE_HCI_ADV_TYPE_ADV_DIRECT_IND_HD (1) - direct advertising - high duty cycle
* * BLE_HCI_ADV_TYPE_ADV_SCAN_IND (2) - indirect scan response
* * BLE_HCI_ADV_TYPE_ADV_NONCONN_IND (3) - indirect advertising - not connectable
* * BLE_HCI_ADV_TYPE_ADV_DIRECT_IND_LD (4) - direct advertising - low duty cycle
*/
uint8_t NimBLEAdvertisedDevice::getAdvType() const {
return m_advType;
@@ -627,18 +617,6 @@ uint8_t NimBLEAdvertisedDevice::getSecondaryPhy() const {
uint16_t NimBLEAdvertisedDevice::getPeriodicInterval() const {
return m_periodicItvl;
} // getPeriodicInterval
/**
* @brief Get the advertisement data status.
* @return The advertisement data status.
* One of:
* * BLE_GAP_EXT_ADV_DATA_STATUS_COMPLETE (0) - Complete extended advertising data
* * BLE_GAP_EXT_ADV_DATA_STATUS_INCOMPLETE (1) - Incomplete extended advertising data, more to come
* * BLE_GAP_EXT_ADV_DATA_STATUS_TRUNCATED (2) - Incomplete extended advertising data, no more to come
*/
uint8_t NimBLEAdvertisedDevice::getDataStatus() const {
return m_dataStatus;
} // getDataStatus
# endif
uint8_t NimBLEAdvertisedDevice::findAdvField(uint8_t type, uint8_t index, size_t* data_loc) const {
@@ -752,7 +730,7 @@ std::string NimBLEAdvertisedDevice::toString() const {
* @brief Get the length of the advertisement data in the payload.
* @return The number of bytes in the payload that is from the advertisement.
*/
uint16_t NimBLEAdvertisedDevice::getAdvLength() const {
uint8_t NimBLEAdvertisedDevice::getAdvLength() const {
return m_advLength;
}
@@ -774,12 +752,11 @@ uint8_t NimBLEAdvertisedDevice::getAddressType() const {
*/
bool NimBLEAdvertisedDevice::isConnectable() const {
# if MYNEWT_VAL(BLE_EXT_ADV)
if (!m_isLegacyAdv) {
return (m_advType & BLE_HCI_ADV_CONN_MASK) || (m_advType & BLE_HCI_ADV_DIRECT_MASK);
if (m_isLegacyAdv) {
return m_advType == BLE_HCI_ADV_RPT_EVTYPE_ADV_IND || m_advType == BLE_HCI_ADV_RPT_EVTYPE_DIR_IND;
}
# endif
return m_advType == BLE_HCI_ADV_RPT_EVTYPE_ADV_IND || m_advType == BLE_HCI_ADV_RPT_EVTYPE_DIR_IND;
return (m_advType & BLE_HCI_ADV_CONN_MASK) || (m_advType & BLE_HCI_ADV_DIRECT_MASK);
} // isConnectable
/**

View File

@@ -69,7 +69,7 @@ class NimBLEAdvertisedDevice {
NimBLEAddress getTargetAddress(uint8_t index = 0) const;
uint8_t getTargetAddressCount() const;
int8_t getTXPower() const;
uint16_t getAdvLength() const;
uint8_t getAdvLength() const;
uint8_t getAddressType() const;
bool isAdvertisingService(const NimBLEUUID& uuid) const;
bool haveAppearance() const;
@@ -92,7 +92,6 @@ class NimBLEAdvertisedDevice {
uint8_t getPrimaryPhy() const;
uint8_t getSecondaryPhy() const;
uint16_t getPeriodicInterval() const;
uint8_t getDataStatus() const;
# endif
operator NimBLEAddress() const;
@@ -162,11 +161,10 @@ class NimBLEAdvertisedDevice {
uint8_t m_advType{};
int8_t m_rssi{};
uint8_t m_callbackSent{};
uint16_t m_advLength{};
uint8_t m_advLength{};
# if MYNEWT_VAL(BLE_EXT_ADV)
bool m_isLegacyAdv{};
uint8_t m_dataStatus{};
uint8_t m_sid{};
uint8_t m_primPhy{};
uint8_t m_secPhy{};

View File

@@ -35,7 +35,7 @@ NimBLEAttValue::NimBLEAttValue(uint16_t init_len, uint16_t max_len)
m_attr_max_len{std::min<uint16_t>(BLE_ATT_ATTR_MAX_LEN, max_len)},
m_attr_len{},
m_capacity{init_len}
# if MYNEWT_VAL(NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED)
# if CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED
,
m_timestamp{}
# endif
@@ -134,7 +134,7 @@ NimBLEAttValue& NimBLEAttValue::append(const uint8_t* value, uint16_t len) {
return *this;
}
# if MYNEWT_VAL(NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED)
# if CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED
time_t t = time(nullptr);
# else
time_t t = 0;

View File

@@ -31,30 +31,20 @@
# include <cstring>
# include <cstdint>
# ifndef MYNEWT_VAL_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED
# ifndef CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED
# define MYNEWT_VAL_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED 0
# else
# define MYNEWT_VAL_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED
# endif
# ifndef CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED
# define CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED 0
# endif
# ifndef BLE_ATT_ATTR_MAX_LEN
# define BLE_ATT_ATTR_MAX_LEN 512
# endif
# ifndef MYNEWT_VAL_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH
# ifndef CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH
# define MYNEWT_VAL_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH 20
# else
# define MYNEWT_VAL_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH
# endif
# endif
# if MYNEWT_VAL(NIMBLE_CPP_ATT_VALUE_INIT_LENGTH) > BLE_ATT_ATTR_MAX_LEN
# error NIMBLE_CPP_ATT_VALUE_INIT_LENGTH cannot be larger than 512 (BLE_ATT_ATTR_MAX_LEN)
# elif MYNEWT_VAL(NIMBLE_CPP_ATT_VALUE_INIT_LENGTH) < 1
# error NIMBLE_CPP_ATT_VALUE_INIT_LENGTH cannot be less than 1; Range = 1 : 512
# if !defined(CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH)
# define CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH 20
# elif CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH > BLE_ATT_ATTR_MAX_LEN
# error CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH cannot be larger than 512 (BLE_ATT_ATTR_MAX_LEN)
# elif CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH < 1
# error CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH cannot be less than 1; Range = 1 : 512
# endif
/* Used to determine if the type passed to a template has a data() and size() method. */
@@ -73,14 +63,6 @@ template <typename T>
struct Has_c_str_length<T, decltype(void(std::declval<T&>().c_str())), decltype(void(std::declval<T&>().length()))>
: std::true_type {};
/* Used to determine if the type passed to a template has a value_type member (std::vector, std::array, std::string, etc.). */
template <typename T, typename = void>
struct Has_value_type : std::false_type {};
template <typename T>
struct Has_value_type<T, decltype(void(sizeof(typename T::value_type)))>
: std::true_type {};
/**
* @brief A specialized container class to hold BLE attribute values.
* @details This class is designed to be more memory efficient than using\n
@@ -92,7 +74,7 @@ class NimBLEAttValue {
uint16_t m_attr_max_len{};
uint16_t m_attr_len{};
uint16_t m_capacity{};
# if MYNEWT_VAL(NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED)
# if CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED
time_t m_timestamp{};
# endif
void deepCopy(const NimBLEAttValue& source);
@@ -103,7 +85,7 @@ class NimBLEAttValue {
* @param[in] init_len The initial size in bytes.
* @param[in] max_len The max size in bytes that the value can be.
*/
NimBLEAttValue(uint16_t init_len = MYNEWT_VAL(NIMBLE_CPP_ATT_VALUE_INIT_LENGTH), uint16_t max_len = BLE_ATT_ATTR_MAX_LEN);
NimBLEAttValue(uint16_t init_len = CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH, uint16_t max_len = BLE_ATT_ATTR_MAX_LEN);
/**
* @brief Construct with an initial value from a buffer.
@@ -188,7 +170,7 @@ class NimBLEAttValue {
/** @brief Iterator end */
const uint8_t* end() const { return m_attr_value + m_attr_len; }
# if MYNEWT_VAL(NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED)
# if CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED
/** @brief Returns a timestamp of when the value was last updated */
time_t getTimeStamp() const { return m_timestamp; }
@@ -228,7 +210,7 @@ class NimBLEAttValue {
const NimBLEAttValue& getValue(time_t* timestamp = nullptr) const {
if (timestamp != nullptr) {
# if MYNEWT_VAL(NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED)
# if CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED
*timestamp = m_timestamp;
# else
*timestamp = 0;
@@ -282,32 +264,13 @@ class NimBLEAttValue {
/**
* @brief Template to set value to the value of <type\>val.
* @param [in] v The <type\>value to set.
* @details Only used if the <type\> has a `data()` and `size()` method with `value_type`.
* Correctly calculates byte size for containers with multi-byte element types.
* @details Only used if the <type\> has a `data()` and `size()` method.
*/
template <typename T>
# ifdef _DOXYGEN_
bool
# else
typename std::enable_if<Has_data_size<T>::value && Has_value_type<T>::value, bool>::type
# endif
setValue(const T& v) {
return setValue(
reinterpret_cast<const uint8_t*>(v.data()),
v.size() * sizeof(typename T::value_type)
);
}
/**
* @brief Template to set value to the value of <type\>val.
* @param [in] v The <type\>value to set.
* @details Only used if the <type\> has a `data()` and `size()` method without `value_type`.
*/
template <typename T>
# ifdef _DOXYGEN_
bool
# else
typename std::enable_if<Has_data_size<T>::value && !Has_value_type<T>::value, bool>::type
typename std::enable_if<Has_data_size<T>::value, bool>::type
# endif
setValue(const T& v) {
return setValue(reinterpret_cast<const uint8_t*>(v.data()), v.size());
@@ -322,11 +285,7 @@ class NimBLEAttValue {
template <typename T>
typename std::enable_if<!std::is_pointer<T>::value, bool>::type setValue(const T& s) {
if constexpr (Has_data_size<T>::value) {
if constexpr (Has_value_type<T>::value) {
return setValue(reinterpret_cast<const uint8_t*>(s.data()), s.size() * sizeof(typename T::value_type));
} else {
return setValue(reinterpret_cast<const uint8_t*>(s.data()), s.size());
}
return setValue(reinterpret_cast<const uint8_t*>(s.data()), s.size());
} else if constexpr (Has_c_str_length<T>::value) {
return setValue(reinterpret_cast<const uint8_t*>(s.c_str()), s.length());
} else {
@@ -348,7 +307,7 @@ class NimBLEAttValue {
template <typename T>
T getValue(time_t* timestamp = nullptr, bool skipSizeCheck = false) const {
if (timestamp != nullptr) {
# if MYNEWT_VAL(NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED)
# if CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED
*timestamp = m_timestamp;
# else
*timestamp = 0;

View File

@@ -15,15 +15,15 @@
* limitations under the License.
*/
#include "NimBLECharacteristic.h"
# include "NimBLECharacteristic.h"
#if CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_PERIPHERAL)
# if defined(CONFIG_NIMBLE_CPP_IDF)
# if !defined(ESP_IDF_VERSION_MAJOR) || ESP_IDF_VERSION_MAJOR < 5
# define ble_gatts_notify_custom ble_gattc_notify_custom
# define ble_gatts_indicate_custom ble_gattc_indicate_custom
# endif
#if defined(CONFIG_NIMBLE_CPP_IDF)
# if !defined(ESP_IDF_VERSION_MAJOR) || ESP_IDF_VERSION_MAJOR < 5
# define ble_gatts_notify_custom ble_gattc_notify_custom
# define ble_gatts_indicate_custom ble_gattc_indicate_custom
# endif
#endif
# include "NimBLE2904.h"
# include "NimBLEDevice.h"
@@ -225,8 +225,7 @@ void NimBLECharacteristic::setService(NimBLEService* pService) {
* @return True if the indication was sent successfully, false otherwise.
*/
bool NimBLECharacteristic::indicate(uint16_t connHandle) const {
auto value{m_value}; // make a copy to avoid issues if the value is changed while indicating
return sendValue(value.data(), value.size(), false, connHandle);
return sendValue(nullptr, 0, false, connHandle);
} // indicate
/**
@@ -248,8 +247,7 @@ bool NimBLECharacteristic::indicate(const uint8_t* value, size_t length, uint16_
* @return True if the notification was sent successfully, false otherwise.
*/
bool NimBLECharacteristic::notify(uint16_t connHandle) const {
auto value{m_value}; // make a copy to avoid issues if the value is changed while notifying
return sendValue(value.data(), value.size(), true, connHandle);
return sendValue(nullptr, 0, true, connHandle);
} // notify
/**
@@ -273,50 +271,55 @@ bool NimBLECharacteristic::notify(const uint8_t* value, size_t length, uint16_t
* @return True if the value was sent successfully, false otherwise.
*/
bool NimBLECharacteristic::sendValue(const uint8_t* value, size_t length, bool isNotification, uint16_t connHandle) const {
ble_npl_hw_enter_critical();
const auto subs = getSubscribers(); // make a copy to avoid issues if subscribers change while sending
ble_npl_hw_exit_critical(0);
int rc = 0;
bool chSpecified = connHandle != BLE_HS_CONN_HANDLE_NONE;
bool requireSecure = m_properties & (BLE_GATT_CHR_F_READ_ENC | BLE_GATT_CHR_F_READ_AUTHEN | BLE_GATT_CHR_F_READ_AUTHOR);
int rc = chSpecified ? BLE_HS_ENOENT : 0; // if handle specified, assume not found until sent
if (value != nullptr && length > 0) { // custom notification value
os_mbuf* om = nullptr;
// Notify all connected peers unless a specific handle is provided
for (const auto& entry : subs) {
uint16_t ch = entry.getConnHandle();
if (ch == BLE_HS_CONN_HANDLE_NONE || (chSpecified && ch != connHandle)) {
continue;
}
if (requireSecure && !entry.isSecured()) {
NIMBLE_LOGW(LOG_TAG, "skipping notify/indicate to connHandle=%d, link not secured", entry.getConnHandle());
continue;
}
// Must re-create the data buffer on each iteration because it is freed by the calls below.
uint8_t retries = 10; // wait up to 10ms for a free buffer
os_mbuf* om = ble_hs_mbuf_from_flat(value, length);
while (!om && --retries) {
ble_npl_time_delay(ble_npl_time_ms_to_ticks32(1));
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_gatts_notify_custom(connHandle, m_handle, om);
} else {
rc = ble_gatts_indicate_custom(connHandle, m_handle, om);
}
goto done;
}
if (!om) {
rc = BLE_HS_ENOMEM;
break;
}
// Notify all connected peers unless a specific handle is provided
for (const auto& ch : NimBLEDevice::getServer()->getPeerDevices()) {
// Must re-create the data buffer on each iteration because it is freed by the calls bellow.
om = ble_hs_mbuf_from_flat(value, length);
if (!om) {
rc = BLE_HS_ENOMEM;
goto done;
}
if (isNotification) {
rc = ble_gatts_notify_custom(ch, m_handle, om);
} else {
rc = ble_gatts_indicate_custom(ch, m_handle, om);
}
}
} else if (connHandle != BLE_HS_CONN_HANDLE_NONE) {
// Null buffer will read the value from the characteristic
if (isNotification) {
rc = ble_gatts_notify_custom(ch, m_handle, om);
rc = ble_gatts_notify_custom(connHandle, m_handle, nullptr);
} else {
rc = ble_gatts_indicate_custom(ch, m_handle, om);
}
if (rc != 0 || chSpecified) {
break;
rc = ble_gatts_indicate_custom(connHandle, m_handle, nullptr);
}
} else { // Notify or indicate to all connected peers the characteristic value
ble_gatts_chr_updated(m_handle);
}
done:
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "failed to send value, rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc));
return false;
@@ -325,107 +328,10 @@ bool NimBLECharacteristic::sendValue(const uint8_t* value, size_t length, bool i
return true;
} // sendValue
/**
* @brief Process a subscription or unsubscription request from a peer.
* @param[in] connInfo A reference to the connection info of the peer.
* @param[in] subVal The subscription value (bitmask).
*/
void NimBLECharacteristic::processSubRequest(NimBLEConnInfo& connInfo, uint8_t subVal) const {
// Only allocate subscribers for characteristics that support notify or indicate.
const uint16_t props = getProperties();
if (!(props & (BLE_GATT_CHR_F_NOTIFY | BLE_GATT_CHR_F_INDICATE))) {
return;
}
auto found = false;
auto firstFree = -1;
for (auto& entry : m_subPeers) {
if (entry.getConnHandle() == connInfo.getConnHandle()) {
found = true;
if (!subVal) {
ble_npl_hw_enter_critical();
entry = SubPeerEntry{}; // unsubscribed, reset entry
ble_npl_hw_exit_critical(0);
}
break;
}
if (firstFree == -1 && entry.getConnHandle() == BLE_HS_CONN_HANDLE_NONE) {
firstFree = &entry - &m_subPeers[0];
}
}
if (!found && subVal) {
if (firstFree >= 0) {
ble_npl_hw_enter_critical();
m_subPeers[firstFree].setConnHandle(connInfo.getConnHandle());
m_subPeers[firstFree].setSubNotify(subVal & 0x1);
m_subPeers[firstFree].setSubIndicate(subVal & 0x2);
m_subPeers[firstFree].setSecured(connInfo.isEncrypted() || connInfo.isAuthenticated() || connInfo.isBonded());
if (m_properties & (BLE_GATT_CHR_F_READ_AUTHEN | BLE_GATT_CHR_F_READ_AUTHOR | BLE_GATT_CHR_F_READ_ENC)) {
// characteristic requires security/authorization
if (!m_subPeers[firstFree].isSecured()) {
m_subPeers[firstFree].setAwaitingSecure(true);
ble_npl_hw_exit_critical(0);
NimBLEDevice::startSecurity(connInfo.getConnHandle());
NIMBLE_LOGD(LOG_TAG,
"Subscription deferred until link is secured for connHandle=%d",
connInfo.getConnHandle());
return;
}
}
ble_npl_hw_exit_critical(0);
} else {
// should never happen, but log just in case
NIMBLE_LOGE(LOG_TAG, "No free subscription slots");
return;
}
}
m_pCallbacks->onSubscribe(const_cast<NimBLECharacteristic*>(this), const_cast<NimBLEConnInfo&>(connInfo), subVal);
}
/**
* @brief Update the security status of a subscribed peer.
* @param[in] peerInfo A reference to the connection info of the peer.
*/
void NimBLECharacteristic::updatePeerStatus(const NimBLEConnInfo& peerInfo) const {
if (!(getProperties() & (NIMBLE_PROPERTY::NOTIFY | NIMBLE_PROPERTY::INDICATE))) {
return;
}
ble_npl_hw_enter_critical();
for (auto& entry : m_subPeers) {
if (entry.getConnHandle() == peerInfo.getConnHandle()) {
entry.setSecured(peerInfo.isEncrypted() || peerInfo.isAuthenticated() || peerInfo.isBonded());
if (entry.isAwaitingSecure()) {
entry.setAwaitingSecure(false);
ble_npl_hw_exit_critical(0);
m_pCallbacks->onSubscribe(const_cast<NimBLECharacteristic*>(this),
const_cast<NimBLEConnInfo&>(peerInfo),
entry.isSubNotify() | (entry.isSubIndicate() << 1));
return;
}
break;
}
}
ble_npl_hw_exit_critical(0);
}
/**
* @brief Handle a read event from a client.
* @param [in] connInfo A reference to a NimBLEConnInfo instance containing the peer info.
*/
void NimBLECharacteristic::readEvent(NimBLEConnInfo& connInfo) {
m_pCallbacks->onRead(this, connInfo);
} // readEvent
/**
* @brief Handle a write event from a client.
* @param [in] val A pointer to the data written by the client.
* @param [in] len The length of the data written by the client.
* @param [in] connInfo A reference to a NimBLEConnInfo instance containing the peer info.
*/
void NimBLECharacteristic::writeEvent(const uint8_t* val, uint16_t len, NimBLEConnInfo& connInfo) {
setValue(val, len);
m_pCallbacks->onWrite(this, connInfo);
@@ -499,18 +405,6 @@ void NimBLECharacteristicCallbacks::onStatus(NimBLECharacteristic* pCharacterist
NIMBLE_LOGD("NimBLECharacteristicCallbacks", "onStatus: default");
} // onStatus
/**
* @brief Callback function to support a Notify/Indicate Status report.
* @param [in] pCharacteristic The characteristic that is the source of the event.
* @param [in] connInfo A reference to a NimBLEConnInfo instance containing the peer info.
* @param [in] code Status return code from the NimBLE stack.
* @details The status code for success is 0 for notifications and BLE_HS_EDONE for indications,
* any other value is an error.
*/
void NimBLECharacteristicCallbacks::onStatus(NimBLECharacteristic* pCharacteristic, NimBLEConnInfo& connInfo, int code) {
NIMBLE_LOGD("NimBLECharacteristicCallbacks", "onStatus: default");
} // onStatus
/**
* @brief Callback function called when a client changes subscription status.
* @param [in] pCharacteristic The characteristic that is the source of the event.

View File

@@ -31,7 +31,6 @@ class NimBLE2904;
# include <string>
# include <vector>
# include <array>
/**
* @brief The model of a BLE Characteristic.
@@ -113,27 +112,7 @@ class NimBLECharacteristic : public NimBLELocalValueAttribute {
}
/**
* @brief Template to send a notification with a value from a class that has a data() and size() method with value_type.
* @param [in] v The value to send.
* @param [in] connHandle Optional, a connection handle to send the notification to.
* @details Correctly calculates byte size for containers with multi-byte element types.
*/
template <typename T>
# ifdef _DOXYGEN_
bool
# else
typename std::enable_if<Has_data_size<T>::value && Has_value_type<T>::value, bool>::type
# endif
notify(const T& v, uint16_t connHandle = BLE_HS_CONN_HANDLE_NONE) const {
return notify(
reinterpret_cast<const uint8_t*>(v.data()),
v.size() * sizeof(typename T::value_type),
connHandle
);
}
/**
* @brief Template to send a notification with a value from a class that has a data() and size() method without value_type.
* @brief Template to send a notification with a value from a class that has a data() and size() method.
* @param [in] v The value to send.
* @param [in] connHandle Optional, a connection handle to send the notification to.
*/
@@ -141,7 +120,7 @@ class NimBLECharacteristic : public NimBLELocalValueAttribute {
# ifdef _DOXYGEN_
bool
# else
typename std::enable_if<Has_data_size<T>::value && !Has_value_type<T>::value, bool>::type
typename std::enable_if<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.data()), v.size(), connHandle);
@@ -181,27 +160,7 @@ class NimBLECharacteristic : public NimBLELocalValueAttribute {
}
/**
* @brief Template to send a indication with a value from a class that has a data() and size() method with value_type.
* @param [in] v The value to send.
* @param [in] connHandle Optional, a connection handle to send the notification to.
* @details Correctly calculates byte size for containers with multi-byte element types.
*/
template <typename T>
# ifdef _DOXYGEN_
bool
# else
typename std::enable_if<Has_data_size<T>::value && Has_value_type<T>::value, bool>::type
# endif
indicate(const T& v, uint16_t connHandle = BLE_HS_CONN_HANDLE_NONE) const {
return indicate(
reinterpret_cast<const uint8_t*>(v.data()),
v.size() * sizeof(typename T::value_type),
connHandle
);
}
/**
* @brief Template to send a indication with a value from a class that has a data() and size() method without value_type.
* @brief Template to send a indication with a value from a class that has a data() and size() method.
* @param [in] v The value to send.
* @param [in] connHandle Optional, a connection handle to send the notification to.
*/
@@ -209,7 +168,7 @@ class NimBLECharacteristic : public NimBLELocalValueAttribute {
# ifdef _DOXYGEN_
bool
# else
typename std::enable_if<Has_data_size<T>::value && !Has_value_type<T>::value, bool>::type
typename std::enable_if<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.data()), v.size(), connHandle);
@@ -231,11 +190,7 @@ class NimBLECharacteristic : public NimBLELocalValueAttribute {
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) {
if constexpr (Has_value_type<T>::value) {
return notify(reinterpret_cast<const uint8_t*>(value.data()), value.size() * sizeof(typename T::value_type), connHandle);
} else {
return notify(reinterpret_cast<const uint8_t*>(value.data()), value.size(), connHandle);
}
return notify(reinterpret_cast<const uint8_t*>(value.data()), value.size(), connHandle);
} else if constexpr (Has_c_str_length<T>::value) {
return notify(reinterpret_cast<const uint8_t*>(value.c_str()), value.length(), connHandle);
} else {
@@ -257,11 +212,7 @@ class NimBLECharacteristic : public NimBLELocalValueAttribute {
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) {
if constexpr (Has_value_type<T>::value) {
return indicate(reinterpret_cast<const uint8_t*>(value.data()), value.size() * sizeof(typename T::value_type), connHandle);
} else {
return indicate(reinterpret_cast<const uint8_t*>(value.data()), value.size(), connHandle);
}
return indicate(reinterpret_cast<const uint8_t*>(value.data()), value.size(), connHandle);
} else if constexpr (Has_c_str_length<T>::value) {
return indicate(reinterpret_cast<const uint8_t*>(value.c_str()), value.length(), connHandle);
} else {
@@ -282,33 +233,9 @@ class NimBLECharacteristic : public NimBLELocalValueAttribute {
bool is_notification = true,
uint16_t connHandle = BLE_HS_CONN_HANDLE_NONE) const;
struct SubPeerEntry {
enum : uint8_t { AWAITING_SECURE = 1 << 0, SECURE = 1 << 1, SUB_NOTIFY = 1 << 2, SUB_INDICATE = 1 << 3 };
void setConnHandle(uint16_t connHandle) { m_connHandle = connHandle; }
uint16_t getConnHandle() const { return m_connHandle; }
void setAwaitingSecure(bool awaiting) { awaiting ? m_flags |= AWAITING_SECURE : m_flags &= ~AWAITING_SECURE; }
void setSecured(bool secure) { secure ? m_flags |= SECURE : m_flags &= ~SECURE; }
void setSubNotify(bool notify) { notify ? m_flags |= SUB_NOTIFY : m_flags &= ~SUB_NOTIFY; }
void setSubIndicate(bool indicate) { indicate ? m_flags |= SUB_INDICATE : m_flags &= ~SUB_INDICATE; }
bool isSubNotify() const { return m_flags & SUB_NOTIFY; }
bool isSubIndicate() const { return m_flags & SUB_INDICATE; }
bool isSecured() const { return m_flags & SECURE; }
bool isAwaitingSecure() const { return m_flags & AWAITING_SECURE; }
private:
uint16_t m_connHandle{BLE_HS_CONN_HANDLE_NONE};
uint8_t m_flags{0};
} __attribute__((packed));
using SubPeerArray = std::array<SubPeerEntry, MYNEWT_VAL(BLE_MAX_CONNECTIONS)>;
SubPeerArray getSubscribers() const { return m_subPeers; }
void processSubRequest(NimBLEConnInfo& connInfo, uint8_t subVal) const;
void updatePeerStatus(const NimBLEConnInfo& peerInfo) const;
NimBLECharacteristicCallbacks* m_pCallbacks{nullptr};
NimBLEService* m_pService{nullptr};
std::vector<NimBLEDescriptor*> m_vDescriptors{};
mutable SubPeerArray m_subPeers{};
}; // NimBLECharacteristic
/**
@@ -323,8 +250,7 @@ class NimBLECharacteristicCallbacks {
virtual ~NimBLECharacteristicCallbacks() {}
virtual void onRead(NimBLECharacteristic* pCharacteristic, NimBLEConnInfo& connInfo);
virtual void onWrite(NimBLECharacteristic* pCharacteristic, NimBLEConnInfo& connInfo);
virtual void onStatus(NimBLECharacteristic* pCharacteristic, int code); // deprecated
virtual void onStatus(NimBLECharacteristic* pCharacteristic, NimBLEConnInfo& connInfo, int code);
virtual void onStatus(NimBLECharacteristic* pCharacteristic, int code);
virtual void onSubscribe(NimBLECharacteristic* pCharacteristic, NimBLEConnInfo& connInfo, uint16_t subValue);
};

View File

@@ -940,11 +940,6 @@ int NimBLEClient::handleGapEvent(struct ble_gap_event* event, void* arg) {
pClient = NimBLEDevice::getClientByPeerAddress(event->disconnect.conn.peer_id_addr);
}
// try by connection handle
if (pClient == nullptr) {
pClient = NimBLEDevice::getClientByHandle(event->disconnect.conn.conn_handle);
}
if (pClient == nullptr) {
NIMBLE_LOGE(LOG_TAG, "Disconnected client not found, conn_handle=%d",
event->disconnect.conn.conn_handle);

View File

@@ -1325,7 +1325,7 @@ std::string NimBLEDevice::toString() {
return getAddress().toString();
} // toString
# if MYNEWT_VAL(NIMBLE_CPP_DEBUG_ASSERT_ENABLED) || __DOXYGEN__
# if CONFIG_NIMBLE_CPP_DEBUG_ASSERT_ENABLED || __DOXYGEN__
/**
* @brief Debug assert - weak function.
* @param [in] file The file where the assert occurred.
@@ -1336,7 +1336,7 @@ void nimble_cpp_assert(const char* file, unsigned line) {
ble_npl_time_delay(10);
abort();
}
# endif // MYNEWT_VAL(NIMBLE_CPP_DEBUG_ASSERT_ENABLED)
# endif // CONFIG_NIMBLE_CPP_DEBUG_ASSERT_ENABLED
void NimBLEDevice::setDeviceCallbacks(NimBLEDeviceCallbacks* cb) {
m_pDeviceCallbacks = cb ? cb : &defaultDeviceCallbacks;

View File

@@ -616,11 +616,6 @@ bool NimBLEExtAdvertisement::setFlags(uint8_t flag) {
* @return True if successful.
*/
bool NimBLEExtAdvertisement::setManufacturerData(const uint8_t* data, size_t length) {
if (length > 0xFF - 1) {
NIMBLE_LOGE(LOG_TAG, "Manufacturer data too long!");
return false;
}
uint8_t header[2];
header[0] = length + 1;
header[1] = BLE_HS_ADV_TYPE_MFG_DATA;
@@ -657,11 +652,6 @@ bool NimBLEExtAdvertisement::setManufacturerData(const std::vector<uint8_t>& dat
* @return True if successful.
*/
bool NimBLEExtAdvertisement::setURI(const std::string& uri) {
if (uri.length() > 0xFF - 1) {
NIMBLE_LOGE(LOG_TAG, "URI too long!");
return false;
}
uint8_t header[2];
header[0] = uri.length() + 1;
header[1] = BLE_HS_ADV_TYPE_URI;
@@ -680,11 +670,6 @@ bool NimBLEExtAdvertisement::setURI(const std::string& uri) {
* @return True if successful.
*/
bool NimBLEExtAdvertisement::setName(const std::string& name, bool isComplete) {
if (name.length() > 0xFF - 1) {
NIMBLE_LOGE(LOG_TAG, "Name too long!");
return false;
}
uint8_t header[2];
header[0] = name.length() + 1;
header[1] = isComplete ? BLE_HS_ADV_TYPE_COMP_NAME : BLE_HS_ADV_TYPE_INCOMP_NAME;
@@ -932,12 +917,8 @@ bool NimBLEExtAdvertisement::setServices(bool complete, uint8_t size, const std:
*/
bool NimBLEExtAdvertisement::setServiceData(const NimBLEUUID& uuid, const uint8_t* data, size_t length) {
uint8_t uuidBytes = uuid.bitSize() / 8;
if (length + uuidBytes + 2 > 0xFF) {
NIMBLE_LOGE(LOG_TAG, "Service data too long!");
return false;
}
uint8_t sDataLen = 2 + uuidBytes + length;
uint8_t sDataLen = 2 + uuidBytes + length;
if (m_payload.size() + sDataLen > MYNEWT_VAL(BLE_EXT_ADV_MAX_SIZE)) {
return false;
}

View File

@@ -101,7 +101,7 @@ class NimBLELocalValueAttribute : public NimBLELocalAttribute, public NimBLEValu
NimBLELocalValueAttribute(const NimBLEUUID& uuid,
uint16_t handle,
uint16_t maxLen,
uint16_t initLen = MYNEWT_VAL(NIMBLE_CPP_ATT_VALUE_INIT_LENGTH))
uint16_t initLen = CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH)
: NimBLELocalAttribute(uuid, handle), NimBLEValueAttribute(maxLen, initLen) {}
/**
* @brief Destroy the NimBLELocalValueAttribute object.

View File

@@ -21,21 +21,12 @@
#include "syscfg/syscfg.h"
#if CONFIG_BT_NIMBLE_ENABLED
# ifndef MYNEWT_VAL_NIMBLE_CPP_LOG_LEVEL
# ifndef CONFIG_NIMBLE_CPP_LOG_LEVEL
# if defined(ARDUINO_ARCH_ESP32) && defined(CORE_DEBUG_LEVEL)
# define MYNEWT_VAL_NIMBLE_CPP_LOG_LEVEL CORE_DEBUG_LEVEL
# else
# define MYNEWT_VAL_NIMBLE_CPP_LOG_LEVEL 0
# endif
# else
# define MYNEWT_VAL_NIMBLE_CPP_LOG_LEVEL CONFIG_NIMBLE_CPP_LOG_LEVEL
# endif
# endif
# if defined(CONFIG_NIMBLE_CPP_IDF)
# include "esp_log.h"
# include "console/console.h"
# ifndef CONFIG_NIMBLE_CPP_LOG_LEVEL
# define CONFIG_NIMBLE_CPP_LOG_LEVEL 0
# endif
# if defined(CONFIG_NIMBLE_CPP_LOG_OVERRIDE_COLOR)
# if CONFIG_LOG_COLORS
@@ -129,13 +120,13 @@
# define NIMBLE_CPP_LOG_PRINT(level, tag, format, ...) \
do { \
if (MYNEWT_VAL(NIMBLE_CPP_LOG_LEVEL) >= level) NIMBLE_CPP_LOG_LEVEL_LOCAL(level, tag, format, ##__VA_ARGS__); \
if (CONFIG_NIMBLE_CPP_LOG_LEVEL >= level) NIMBLE_CPP_LOG_LEVEL_LOCAL(level, tag, format, ##__VA_ARGS__); \
} while (0)
# else
# define NIMBLE_CPP_LOG_PRINT(level, tag, format, ...) \
do { \
if (MYNEWT_VAL(NIMBLE_CPP_LOG_LEVEL) >= level) ESP_LOG_LEVEL_LOCAL(level, tag, format, ##__VA_ARGS__); \
if (CONFIG_NIMBLE_CPP_LOG_LEVEL >= level) ESP_LOG_LEVEL_LOCAL(level, tag, format, ##__VA_ARGS__); \
} while (0)
# endif /* CONFIG_NIMBLE_CPP_LOG_OVERRIDE_COLOR */
@@ -146,27 +137,35 @@
# define NIMBLE_LOGE(tag, format, ...) NIMBLE_CPP_LOG_PRINT(ESP_LOG_ERROR, tag, format, ##__VA_ARGS__)
# else
# include "syscfg/syscfg.h"
# include "nimble/console/console.h"
# ifndef CONFIG_NIMBLE_CPP_LOG_LEVEL
# if defined(ARDUINO_ARCH_ESP32) && defined(CORE_DEBUG_LEVEL)
# define CONFIG_NIMBLE_CPP_LOG_LEVEL CORE_DEBUG_LEVEL
# else
# define CONFIG_NIMBLE_CPP_LOG_LEVEL 0
# endif
# endif
# if MYNEWT_VAL(NIMBLE_CPP_LOG_LEVEL) >= 4
# if CONFIG_NIMBLE_CPP_LOG_LEVEL >= 4
# define NIMBLE_LOGD(tag, format, ...) console_printf("D %s: " format "\n", tag, ##__VA_ARGS__)
# else
# define NIMBLE_LOGD(tag, format, ...) (void)tag
# endif
# if MYNEWT_VAL(NIMBLE_CPP_LOG_LEVEL) >= 3
# if CONFIG_NIMBLE_CPP_LOG_LEVEL >= 3
# define NIMBLE_LOGI(tag, format, ...) console_printf("I %s: " format "\n", tag, ##__VA_ARGS__)
# else
# define NIMBLE_LOGI(tag, format, ...) (void)tag
# endif
# if MYNEWT_VAL(NIMBLE_CPP_LOG_LEVEL) >= 2
# if CONFIG_NIMBLE_CPP_LOG_LEVEL >= 2
# define NIMBLE_LOGW(tag, format, ...) console_printf("W %s: " format "\n", tag, ##__VA_ARGS__)
# else
# define NIMBLE_LOGW(tag, format, ...) (void)tag
# endif
# if MYNEWT_VAL(NIMBLE_CPP_LOG_LEVEL) >= 1
# if CONFIG_NIMBLE_CPP_LOG_LEVEL >= 1
# define NIMBLE_LOGE(tag, format, ...) console_printf("E %s: " format "\n", tag, ##__VA_ARGS__)
# else
# define NIMBLE_LOGE(tag, format, ...) (void)tag

View File

@@ -109,34 +109,13 @@ class NimBLERemoteValueAttribute : public NimBLEValueAttribute, public NimBLEAtt
* @brief Template to set the remote characteristic value to <type\>val.
* @param [in] v The value to write.
* @param [in] response True == request write response.
* @details Only used if the <type\> has a `data()` and `size()` method with `value_type`.
* Correctly calculates byte size for containers with multi-byte element types.
* @details Only used if the <type\> has a `data()` and `size()` method.
*/
template <typename T>
# ifdef _DOXYGEN_
bool
# else
typename std::enable_if<Has_data_size<T>::value && Has_value_type<T>::value, bool>::type
# endif
writeValue(const T& v, bool response = false) const {
return writeValue(
reinterpret_cast<const uint8_t*>(v.data()),
v.size() * sizeof(typename T::value_type),
response
);
}
/**
* @brief Template to set the remote characteristic value to <type\>val.
* @param [in] v The value to write.
* @param [in] response True == request write response.
* @details Only used if the <type\> has a `data()` and `size()` method without `value_type`.
*/
template <typename T>
# ifdef _DOXYGEN_
bool
# else
typename std::enable_if<Has_data_size<T>::value && !Has_value_type<T>::value, bool>::type
typename std::enable_if<Has_data_size<T>::value, bool>::type
# endif
writeValue(const T& v, bool response = false) const {
return writeValue(reinterpret_cast<const uint8_t*>(v.data()), v.size(), response);
@@ -152,11 +131,7 @@ class NimBLERemoteValueAttribute : public NimBLEValueAttribute, public NimBLEAtt
template <typename T>
typename std::enable_if<!std::is_pointer<T>::value, bool>::type writeValue(const T& v, bool response = false) const {
if constexpr (Has_data_size<T>::value) {
if constexpr (Has_value_type<T>::value) {
return writeValue(reinterpret_cast<const uint8_t*>(v.data()), v.size() * sizeof(typename T::value_type), response);
} else {
return writeValue(reinterpret_cast<const uint8_t*>(v.data()), v.size(), response);
}
return writeValue(reinterpret_cast<const uint8_t*>(v.data()), v.size(), response);
} else if constexpr (Has_c_str_length<T>::value) {
return writeValue(reinterpret_cast<const uint8_t*>(v.c_str()), v.length(), response);
} else {

View File

@@ -117,22 +117,13 @@ int NimBLEScan::handleGapEvent(ble_gap_event* event, void* arg) {
NIMBLE_LOGI(LOG_TAG, "New advertiser: %s", advertisedAddress.toString().c_str());
} else {
advertisedDevice->update(event, event_type);
if (isLegacyAdv) {
if (event_type == BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP) {
NIMBLE_LOGI(LOG_TAG, "Scan response from: %s", advertisedAddress.toString().c_str());
} else {
NIMBLE_LOGI(LOG_TAG, "Duplicate; updated: %s", advertisedAddress.toString().c_str());
}
if (isLegacyAdv && event_type == BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP) {
NIMBLE_LOGI(LOG_TAG, "Scan response from: %s", advertisedAddress.toString().c_str());
} else {
NIMBLE_LOGI(LOG_TAG, "Duplicate; updated: %s", advertisedAddress.toString().c_str());
}
}
# if MYNEWT_VAL(BLE_EXT_ADV)
if (advertisedDevice->getDataStatus() == BLE_GAP_EXT_ADV_DATA_STATUS_INCOMPLETE) {
NIMBLE_LOGD(LOG_TAG, "EXT ADV data incomplete, waiting for more");
return 0;
}
# endif
if (!advertisedDevice->m_callbackSent) {
advertisedDevice->m_callbackSent++;
pScan->m_pScanCallbacks->onDiscovered(advertisedDevice);
@@ -498,7 +489,7 @@ void NimBLEScan::clearResults() {
* @brief Dump the scan results to the log.
*/
void NimBLEScanResults::dump() const {
# if MYNEWT_VAL(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());
}

View File

@@ -143,25 +143,10 @@ NimBLEService* NimBLEServer::getServiceByHandle(uint16_t handle) const {
return nullptr;
}
/**
* @brief Get a BLE Characteristic by its handle
* @param handle The handle of the characteristic.
* @return A pointer to the characteristic object or nullptr if not found.
*/
NimBLECharacteristic* NimBLEServer::getCharacteristicByHandle(uint16_t handle) const {
for (const auto& svc : m_svcVec) {
NimBLECharacteristic* pChr = svc->getCharacteristicByHandle(handle);
if (pChr != nullptr) {
return pChr;
}
}
return nullptr;
} // getCharacteristicByHandle
# if MYNEWT_VAL(BLE_EXT_ADV)
/**
* @brief Retrieve the advertising object that can be used to advertise the existence of the server.
* @return A pointer to an advertising object.
* @return A pinter to an advertising object.
*/
NimBLEExtAdvertising* NimBLEServer::getAdvertising() const {
return NimBLEDevice::getAdvertising();
@@ -204,7 +189,7 @@ void NimBLEServer::start() {
return;
}
# if MYNEWT_VAL(NIMBLE_CPP_LOG_LEVEL) >= 4
# if CONFIG_NIMBLE_CPP_LOG_LEVEL >= 4
ble_gatts_show_local();
# endif
@@ -320,7 +305,7 @@ NimBLEConnInfo NimBLEServer::getPeerInfo(uint8_t index) const {
for (const auto& peer : m_connectedPeers) {
if (peer != BLE_HS_CONN_HANDLE_NONE) {
if (count == index) {
return getPeerInfoByHandle(peer);
return getPeerInfoByHandle(m_connectedPeers[count]);
}
count++;
}
@@ -375,7 +360,9 @@ int NimBLEServer::handleGapEvent(ble_gap_event* event, void* arg) {
}
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "Connection failed rc = %d %s", rc, NimBLEUtils::returnCodeToString(rc));
NIMBLE_LOGE(LOG_TAG, "Connection failed rc = %d %s",
rc,
NimBLEUtils::returnCodeToString(rc));
# if !MYNEWT_VAL(BLE_EXT_ADV) && MYNEWT_VAL(BLE_ROLE_BROADCASTER)
NimBLEDevice::startAdvertising();
# endif
@@ -442,21 +429,33 @@ int NimBLEServer::handleGapEvent(ble_gap_event* event, void* arg) {
} // BLE_GAP_EVENT_DISCONNECT
case BLE_GAP_EVENT_SUBSCRIBE: {
rc = ble_gap_conn_find(event->subscribe.conn_handle, &peerInfo.m_desc);
if (rc != 0) {
break;
NIMBLE_LOGI(LOG_TAG,
"subscribe event; attr_handle=%d, subscribed: %s",
event->subscribe.attr_handle,
((event->subscribe.cur_notify || event->subscribe.cur_indicate) ? "true" : "false"));
for (const auto& svc : pServer->m_svcVec) {
for (const auto& chr : svc->m_vChars) {
if (chr->getHandle() == event->subscribe.attr_handle) {
rc = ble_gap_conn_find(event->subscribe.conn_handle, &peerInfo.m_desc);
if (rc != 0) {
break;
}
auto chrProps = chr->getProperties();
if (!peerInfo.isEncrypted() &&
(chrProps & BLE_GATT_CHR_F_READ_AUTHEN || chrProps & BLE_GATT_CHR_F_READ_AUTHOR ||
chrProps & BLE_GATT_CHR_F_READ_ENC)) {
NimBLEDevice::startSecurity(event->subscribe.conn_handle);
}
chr->m_pCallbacks->onSubscribe(chr,
peerInfo,
event->subscribe.cur_notify + (event->subscribe.cur_indicate << 1));
}
}
}
uint8_t subVal = event->subscribe.cur_notify + (event->subscribe.cur_indicate << 1);
NIMBLE_LOGI(LOG_TAG, "subscribe event; attr_handle=%d, subscribed: %d", event->subscribe.attr_handle, subVal);
auto pChar = pServer->getCharacteristicByHandle(event->subscribe.attr_handle);
if (!pChar) {
NIMBLE_LOGE(LOG_TAG, "subscribe event; attr_handle=%d, not found", event->subscribe.attr_handle);
break;
}
pChar->processSubRequest(peerInfo, subVal);
break;
} // BLE_GAP_EVENT_SUBSCRIBE
@@ -470,14 +469,18 @@ int NimBLEServer::handleGapEvent(ble_gap_event* event, void* arg) {
} // BLE_GAP_EVENT_MTU
case BLE_GAP_EVENT_NOTIFY_TX: {
rc = ble_gap_conn_find(event->notify_tx.conn_handle, &peerInfo.m_desc);
if (rc != 0) {
break;
NimBLECharacteristic* pChar = nullptr;
for (const auto& svc : pServer->m_svcVec) {
for (auto& chr : svc->m_vChars) {
if (chr->getHandle() == event->notify_tx.attr_handle) {
pChar = chr;
}
}
}
auto pChar = pServer->getCharacteristicByHandle(event->notify_tx.attr_handle);
if (pChar == nullptr) {
break;
return 0;
}
if (event->notify_tx.indication) {
@@ -487,19 +490,9 @@ int NimBLEServer::handleGapEvent(ble_gap_event* event, void* arg) {
}
pChar->m_pCallbacks->onStatus(pChar, event->notify_tx.status);
pChar->m_pCallbacks->onStatus(pChar, peerInfo, event->notify_tx.status);
break;
} // BLE_GAP_EVENT_NOTIFY_TX
# if MYNEWT_VAL(BLE_ROLE_CENTRAL)
case BLE_GAP_EVENT_NOTIFY_RX: {
if (pServer->m_pClient && pServer->m_pClient->m_connHandle == event->notify_rx.conn_handle) {
NimBLEClient::handleGapEvent(event, pServer->m_pClient);
}
break;
} // BLE_GAP_EVENT_NOTIFY_RX
# endif
case BLE_GAP_EVENT_ADV_COMPLETE: {
# if MYNEWT_VAL(BLE_EXT_ADV) && MYNEWT_VAL(BLE_ROLE_BROADCASTER)
case BLE_GAP_EVENT_SCAN_REQ_RCVD:
@@ -549,13 +542,6 @@ int NimBLEServer::handleGapEvent(ble_gap_event* event, void* arg) {
NimBLEClient::handleGapEvent(event, pServer->m_pClient);
}
# endif
// update the secured status of the peer in each characteristic's subscribed peers list
for (const auto& svc : pServer->m_svcVec) {
for (const auto& chr : svc->m_vChars) {
chr->updatePeerStatus(peerInfo);
}
}
break;
} // BLE_GAP_EVENT_ENC_CHANGE

View File

@@ -72,7 +72,6 @@ class NimBLEServer {
NimBLEService* getServiceByUUID(const char* uuid, uint16_t instanceId = 0) const;
NimBLEService* getServiceByUUID(const NimBLEUUID& uuid, uint16_t instanceId = 0) const;
NimBLEService* getServiceByHandle(uint16_t handle) const;
NimBLECharacteristic* getCharacteristicByHandle(uint16_t handle) const;
void removeService(NimBLEService* service, bool deleteSvc = false);
void addService(NimBLEService* service);
uint16_t getPeerMTU(uint16_t connHandle) const;
@@ -119,10 +118,6 @@ class NimBLEServer {
NimBLEServer();
~NimBLEServer();
static int handleGapEvent(struct ble_gap_event* event, void* arg);
static int handleGattEvent(uint16_t connHandle, uint16_t attrHandle, ble_gatt_access_ctxt* ctxt, void* arg);
void serviceChanged();
void resetGATT();
bool m_gattsStarted : 1;
bool m_svcChanged : 1;
@@ -130,13 +125,19 @@ class NimBLEServer {
# if !MYNEWT_VAL(BLE_EXT_ADV)
bool m_advertiseOnDisconnect : 1;
# endif
NimBLEServerCallbacks* m_pServerCallbacks;
std::vector<NimBLEService*> m_svcVec;
NimBLEServerCallbacks* m_pServerCallbacks;
std::vector<NimBLEService*> m_svcVec;
std::array<uint16_t, MYNEWT_VAL(BLE_MAX_CONNECTIONS)> m_connectedPeers;
# if MYNEWT_VAL(BLE_ROLE_CENTRAL)
NimBLEClient* m_pClient{nullptr};
# endif
static int handleGapEvent(struct ble_gap_event* event, void* arg);
static int handleGattEvent(uint16_t connHandle, uint16_t attrHandle, ble_gatt_access_ctxt* ctxt, void* arg);
void serviceChanged();
void resetGATT();
}; // NimBLEServer
/**

View File

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

View File

@@ -1,220 +0,0 @@
/*
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifdef ESP_PLATFORM
# ifndef NIMBLE_CPP_STREAM_H
# define NIMBLE_CPP_STREAM_H
# include "syscfg/syscfg.h"
# if CONFIG_BT_NIMBLE_ENABLED && (MYNEWT_VAL(BLE_ROLE_PERIPHERAL) || MYNEWT_VAL(BLE_ROLE_CENTRAL))
# include "NimBLEUUID.h"
# include <freertos/FreeRTOS.h>
# include <freertos/ringbuf.h>
# if NIMBLE_CPP_ARDUINO_STRING_AVAILABLE
# include <Stream.h>
# else
// Minimal Stream/Print stubs when Arduino not available
class Print {
public:
virtual ~Print() {}
virtual size_t write(uint8_t) = 0;
virtual size_t write(const uint8_t* buffer, size_t size) = 0;
size_t print(const char* s);
size_t println(const char* s);
size_t printf(const char* format, ...) __attribute__((format(printf, 2, 3)));
};
class Stream : public Print {
public:
virtual int available() = 0;
virtual int read() = 0;
virtual int peek() = 0;
void setTimeout(unsigned long timeout) { m_timeout = timeout; }
unsigned long getTimeout() const { return m_timeout; }
protected:
unsigned long m_timeout{0};
};
# endif
class NimBLEStream : public Stream {
public:
NimBLEStream() = default;
virtual ~NimBLEStream() { end(); }
bool begin();
bool end();
// Configure TX/RX buffer sizes and task parameters before begin()
void setTxBufSize(uint32_t size) { m_txBufSize = size; }
void setRxBufSize(uint32_t size) { m_rxBufSize = size; }
void setTxTaskStackSize(uint32_t size) { m_txTaskStackSize = size; }
void setTxTaskPriority(uint32_t priority) { m_txTaskPriority = priority; }
// Print/Stream TX methods
virtual size_t write(const uint8_t* data, size_t len) override;
virtual size_t write(uint8_t data) override { return write(&data, 1); }
size_t availableForWrite() const;
void flush() override;
// Stream RX methods
virtual int available() override;
virtual int read() override;
virtual int peek() override;
// Serial-like helpers
bool ready() const { return isReady(); }
operator bool() const { return ready(); }
using Print::write;
protected:
static void txTask(void* arg);
virtual bool send(const uint8_t* data, size_t len) = 0;
virtual bool isReady() const = 0;
// Push received data into RX ring (called by subclass callbacks)
size_t pushRx(const uint8_t* data, size_t len);
RingbufHandle_t m_txBuf{nullptr};
RingbufHandle_t m_rxBuf{nullptr};
TaskHandle_t m_txTask{nullptr};
uint32_t m_txTaskStackSize{4096};
uint32_t m_txTaskPriority{tskIDLE_PRIORITY + 1};
uint32_t m_txBufSize{1024};
uint32_t m_rxBufSize{1024};
// RX peek state
mutable uint8_t m_peekByte{0};
mutable bool m_hasPeek{false};
};
# if MYNEWT_VAL(BLE_ROLE_PERIPHERAL)
# include "NimBLECharacteristic.h"
class NimBLEStreamServer : public NimBLEStream {
public:
NimBLEStreamServer() : m_charCallbacks(this) {}
~NimBLEStreamServer() = default;
// non-copyable
NimBLEStreamServer(const NimBLEStreamServer&) = delete;
NimBLEStreamServer& operator=(const NimBLEStreamServer&) = delete;
bool init(const NimBLEUUID& svcUuid = NimBLEUUID(uint16_t(0xc0de)),
const NimBLEUUID& chrUuid = NimBLEUUID(uint16_t(0xfeed)),
bool canWrite = false,
bool secure = false);
void deinit();
size_t write(const uint8_t* data, size_t len) override;
uint16_t getPeerHandle() const { return m_charCallbacks.m_peerHandle; }
bool hasSubscriber() const { return m_charCallbacks.m_peerHandle != BLE_HS_CONN_HANDLE_NONE; }
size_t getMaxLength() const { return m_charCallbacks.m_maxLen; }
void setCallbacks(NimBLECharacteristicCallbacks* pCallbacks) { m_charCallbacks.m_userCallbacks = pCallbacks; }
private:
bool send(const uint8_t* data, size_t len) override;
bool isReady() const override { return hasSubscriber(); }
struct ChrCallbacks : public NimBLECharacteristicCallbacks {
ChrCallbacks(NimBLEStreamServer* parent)
: m_parent(parent), m_userCallbacks(nullptr), m_peerHandle(BLE_HS_CONN_HANDLE_NONE), m_maxLen(0) {}
void onWrite(NimBLECharacteristic* pCharacteristic, NimBLEConnInfo& connInfo) override;
void onSubscribe(NimBLECharacteristic* pCharacteristic, NimBLEConnInfo& connInfo, uint16_t subValue) override;
void onStatus(NimBLECharacteristic* pCharacteristic, int code) override;
// override this to avoid recursion when debug logs are enabled
void onStatus(NimBLECharacteristic* pCharacteristic, NimBLEConnInfo& connInfo, int code) {
if (m_userCallbacks) {
m_userCallbacks->onStatus(pCharacteristic, connInfo, code);
}
}
NimBLEStreamServer* m_parent;
NimBLECharacteristicCallbacks* m_userCallbacks;
uint16_t m_peerHandle;
uint16_t m_maxLen;
} m_charCallbacks;
NimBLECharacteristic* m_pChr{nullptr};
int m_rc{0};
};
# endif // BLE_ROLE_PERIPHERAL
# if MYNEWT_VAL(BLE_ROLE_CENTRAL)
# include "NimBLERemoteCharacteristic.h"
class NimBLEStreamClient : public NimBLEStream {
public:
NimBLEStreamClient() = default;
~NimBLEStreamClient() = default;
// non-copyable
NimBLEStreamClient(const NimBLEStreamClient&) = delete;
NimBLEStreamClient& operator=(const NimBLEStreamClient&) = delete;
// Attach a discovered remote characteristic; app owns discovery/connection.
// Set subscribeNotify=true to receive notifications into RX buffer.
bool init(NimBLERemoteCharacteristic* pChr, bool subscribeNotify = false);
void deinit();
size_t write(const uint8_t* data, size_t len) override;
void setWriteWithResponse(bool useWithRsp) { m_writeWithRsp = useWithRsp; }
void setNotifyCallback(NimBLERemoteCharacteristic::notify_callback cb) { m_userNotifyCallback = cb; }
private:
bool send(const uint8_t* data, size_t len) override;
bool isReady() const override { return m_pChr != nullptr; }
void notifyCallback(NimBLERemoteCharacteristic* pChar, uint8_t* pData, size_t len, bool isNotify);
NimBLERemoteCharacteristic* m_pChr{nullptr};
bool m_writeWithRsp{false};
NimBLERemoteCharacteristic::notify_callback m_userNotifyCallback{nullptr};
};
# endif // BLE_ROLE_CENTRAL
# endif // CONFIG_BT_NIMBLE_ENABLED && (MYNEWT_VAL(BLE_ROLE_PERIPHERAL) || MYNEWT_VAL(BLE_ROLE_CENTRAL))
// These logging macros exist to provide log output over UART so that it stream classes can
// be used to redirect logs without causing recursion issues.
static int uart_log_printfv(const char* format, va_list arg);
static int uart_log_printf(const char* format, ...);
# if MYNEWT_VAL(NIMBLE_CPP_LOG_LEVEL) >= 4
# define NIMBLE_UART_LOGD(tag, format, ...) uart_log_printf("D %s: " format "\n", tag, ##__VA_ARGS__)
# else
# define NIMBLE_UART_LOGD(tag, format, ...) (void)tag
# endif
# if MYNEWT_VAL(NIMBLE_CPP_LOG_LEVEL) >= 3
# define NIMBLE_UART_LOGI(tag, format, ...) uart_log_printf("I %s: " format "\n", tag, ##__VA_ARGS__)
# else
# define NIMBLE_UART_LOGI(tag, format, ...) (void)tag
# endif
# if MYNEWT_VAL(NIMBLE_CPP_LOG_LEVEL) >= 2
# define NIMBLE_UART_LOGW(tag, format, ...) uart_log_printf("W %s: " format "\n", tag, ##__VA_ARGS__)
# else
# define NIMBLE_UART_LOGW(tag, format, ...) (void)tag
# endif
# if MYNEWT_VAL(NIMBLE_CPP_LOG_LEVEL) >= 1
# define NIMBLE_UART_LOGE(tag, format, ...) uart_log_printf("E %s: " format "\n", tag, ##__VA_ARGS__)
# else
# define NIMBLE_UART_LOGE(tag, format, ...) (void)tag
# endif
# endif // NIMBLE_CPP_STREAM_H
#endif // ESP_PLATFORM

View File

@@ -35,42 +35,14 @@
# include <stdlib.h>
# include <climits>
# ifndef MYNEWT_VAL_NIMBLE_CPP_ENABLE_RETURN_CODE_TEXT
# ifdef CONFIG_NIMBLE_CPP_ENABLE_RETURN_CODE_TEXT
# define MYNEWT_VAL_NIMBLE_CPP_ENABLE_RETURN_CODE_TEXT CONFIG_NIMBLE_CPP_ENABLE_RETURN_CODE_TEXT
# else
# define MYNEWT_VAL_NIMBLE_CPP_ENABLE_RETURN_CODE_TEXT (0)
# endif
# endif
# ifndef MYNEWT_VAL_NIMBLE_CPP_ENABLE_GAP_EVENT_CODE_TEXT
# ifdef CONFIG_NIMBLE_CPP_ENABLE_GAP_EVENT_CODE_TEXT
# define MYNEWT_VAL_NIMBLE_CPP_ENABLE_GAP_EVENT_CODE_TEXT CONFIG_NIMBLE_CPP_ENABLE_GAP_EVENT_CODE_TEXT
# else
# define MYNEWT_VAL_NIMBLE_CPP_ENABLE_GAP_EVENT_CODE_TEXT (0)
# endif
# endif
# ifndef MYNEWT_VAL_NIMBLE_CPP_ENABLE_ADVERTISEMENT_TYPE_TEXT
# ifdef CONFIG_NIMBLE_CPP_ENABLE_ADVERTISEMENT_TYPE_TEXT
# define MYNEWT_VAL_NIMBLE_CPP_ENABLE_ADVERTISEMENT_TYPE_TEXT CONFIG_NIMBLE_CPP_ENABLE_ADVERTISEMENT_TYPE_TEXT
# else
# define MYNEWT_VAL_NIMBLE_CPP_ENABLE_ADVERTISEMENT_TYPE_TEXT (0)
# endif
# endif
# if defined INC_FREERTOS_H
# ifndef MYNEWT_VAL_NIMBLE_CPP_FREERTOS_TASK_BLOCK_BIT
# ifndef CONFIG_NIMBLE_CPP_FREERTOS_TASK_BLOCK_BIT
# define MYNEWT_VAL_NIMBLE_CPP_FREERTOS_TASK_BLOCK_BIT 31
# else
# define MYNEWT_VAL_NIMBLE_CPP_FREERTOS_TASK_BLOCK_BIT CONFIG_NIMBLE_CPP_FREERTOS_TASK_BLOCK_BIT
# endif
# ifndef CONFIG_NIMBLE_CPP_FREERTOS_TASK_BLOCK_BIT
# define CONFIG_NIMBLE_CPP_FREERTOS_TASK_BLOCK_BIT 31
# endif
constexpr uint32_t TASK_BLOCK_BIT = (1 << CONFIG_NIMBLE_CPP_FREERTOS_TASK_BLOCK_BIT);
# endif
constexpr uint32_t TASK_BLOCK_BIT = (1 << MYNEWT_VAL(NIMBLE_CPP_FREERTOS_TASK_BLOCK_BIT));
static const char* LOG_TAG = "NimBLEUtils";
static const char* LOG_TAG = "NimBLEUtils";
/**
* @brief Construct a NimBLETaskData instance.
@@ -82,7 +54,7 @@ NimBLETaskData::NimBLETaskData(void* pInstance, int flags, void* buf)
: m_pInstance{pInstance},
m_flags{flags},
m_pBuf{buf}
# ifdef INC_FREERTOS_H
# if defined INC_FREERTOS_H
,
m_pHandle{xTaskGetCurrentTaskHandle()} {
}
@@ -103,7 +75,7 @@ NimBLETaskData::NimBLETaskData(void* pInstance, int flags, void* buf)
* @brief Destructor.
*/
NimBLETaskData::~NimBLETaskData() {
# ifndef INC_FREERTOS_H
# if !defined INC_FREERTOS_H
if (m_pHandle != nullptr) {
ble_npl_sem_deinit(static_cast<ble_npl_sem*>(m_pHandle));
delete static_cast<ble_npl_sem*>(m_pHandle);
@@ -125,7 +97,7 @@ bool NimBLEUtils::taskWait(const NimBLETaskData& taskData, uint32_t timeout) {
ble_npl_time_ms_to_ticks(timeout, &ticks);
}
# ifdef INC_FREERTOS_H
# if defined INC_FREERTOS_H
uint32_t notificationValue;
xTaskNotifyWait(0, TASK_BLOCK_BIT, &notificationValue, 0);
if (notificationValue & TASK_BLOCK_BIT) {
@@ -147,7 +119,7 @@ bool NimBLEUtils::taskWait(const NimBLETaskData& taskData, uint32_t timeout) {
void NimBLEUtils::taskRelease(const NimBLETaskData& taskData, int flags) {
taskData.m_flags = flags;
if (taskData.m_pHandle != nullptr) {
# ifdef INC_FREERTOS_H
# if defined INC_FREERTOS_H
xTaskNotify(static_cast<TaskHandle_t>(taskData.m_pHandle), TASK_BLOCK_BIT, eSetBits);
# else
ble_npl_sem_release(static_cast<ble_npl_sem*>(taskData.m_pHandle));
@@ -161,7 +133,7 @@ void NimBLEUtils::taskRelease(const NimBLETaskData& taskData, int flags) {
* @return A string representation of the return code.
*/
const char* NimBLEUtils::returnCodeToString(int rc) {
# if MYNEWT_VAL(NIMBLE_CPP_ENABLE_RETURN_CODE_TEXT)
# if defined(CONFIG_NIMBLE_CPP_ENABLE_RETURN_CODE_TEXT)
switch (rc) {
case 0:
return "SUCCESS";
@@ -444,10 +416,10 @@ const char* NimBLEUtils::returnCodeToString(int rc) {
default:
return "Unknown";
}
# else // MYNEWT_VAL(NIMBLE_CPP_ENABLE_RETURN_CODE_TEXT)
# else // #if defined(CONFIG_NIMBLE_CPP_ENABLE_RETURN_CODE_TEXT)
(void)rc;
return "";
# endif // MYNEWT_VAL(NIMBLE_CPP_ENABLE_RETURN_CODE_TEXT)
# endif // #if defined(CONFIG_NIMBLE_CPP_ENABLE_RETURN_CODE_TEXT)
}
/**
@@ -456,7 +428,7 @@ const char* NimBLEUtils::returnCodeToString(int rc) {
* @return A string representation of the advertising flags.
*/
const char* NimBLEUtils::advTypeToString(uint8_t advType) {
# if MYNEWT_VAL(NIMBLE_CPP_ENABLE_ADVERTISEMENT_TYPE_TEXT)
# if defined(CONFIG_NIMBLE_CPP_ENABLE_ADVERTISEMENT_TYPE_TEXT)
switch (advType) {
case BLE_HCI_ADV_TYPE_ADV_IND: // 0
return "Undirected - Connectable / Scannable";
@@ -471,10 +443,10 @@ const char* NimBLEUtils::advTypeToString(uint8_t advType) {
default:
return "Unknown flag";
}
# else // MYNEWT_VAL(NIMBLE_CPP_ENABLE_ADVERTISEMENT_TYPE_TEXT)
# else // #if defined(CONFIG_NIMBLE_CPP_ENABLE_ADVERTISEMENT_TYPE_TEXT)
(void)advType;
return "";
# endif // MYNEWT_VAL(NIMBLE_CPP_ENABLE_ADVERTISEMENT_TYPE_TEXT)
# endif // #if defined(CONFIG_NIMBLE_CPP_ENABLE_ADVERTISEMENT_TYPE_TEXT)
} // adFlagsToString
/**
@@ -483,7 +455,7 @@ const char* NimBLEUtils::advTypeToString(uint8_t advType) {
* @return A string representation of the event type.
*/
const char* NimBLEUtils::gapEventToString(uint8_t eventType) {
# if MYNEWT_VAL(NIMBLE_CPP_ENABLE_GAP_EVENT_CODE_TEXT)
# if defined(CONFIG_NIMBLE_CPP_ENABLE_GAP_EVENT_CODE_TEXT)
switch (eventType) {
case BLE_GAP_EVENT_CONNECT: // 0
return "BLE_GAP_EVENT_CONNECT ";
@@ -563,10 +535,10 @@ const char* NimBLEUtils::gapEventToString(uint8_t eventType) {
NIMBLE_LOGD(LOG_TAG, "Unknown event type %d 0x%.2x", eventType, eventType);
return "Unknown event type";
}
# else // MYNEWT_VAL(NIMBLE_CPP_ENABLE_GAP_EVENT_CODE_TEXT)
# else // #if defined(CONFIG_NIMBLE_CPP_ENABLE_GAP_EVENT_CODE_TEXT)
(void)eventType;
return "";
# endif // MYNEWT_VAL(NIMBLE_CPP_ENABLE_GAP_EVENT_CODE_TEXT)
# endif // #if defined(CONFIG_NIMBLE_CPP_ENABLE_GAP_EVENT_CODE_TEXT)
} // gapEventToString
/**

View File

@@ -21,15 +21,7 @@
#include "syscfg/syscfg.h"
#if CONFIG_BT_NIMBLE_ENABLED
# ifndef MYNEWT_VAL_NIMBLE_CPP_DEBUG_ASSERT_ENABLED
# if defined(CONFIG_NIMBLE_CPP_DEBUG_ASSERT_ENABLED)
# define MYNEWT_VAL_NIMBLE_CPP_DEBUG_ASSERT_ENABLED CONFIG_NIMBLE_CPP_DEBUG_ASSERT_ENABLED
# else
# define MYNEWT_VAL_NIMBLE_CPP_DEBUG_ASSERT_ENABLED (0)
# endif
# endif
#if MYNEWT_VAL(NIMBLE_CPP_DEBUG_ASSERT_ENABLED) && !defined NDEBUG
#if CONFIG_NIMBLE_CPP_DEBUG_ASSERT_ENABLED && !defined NDEBUG
void nimble_cpp_assert(const char *file, unsigned line) __attribute((weak, noreturn));
# define NIMBLE_ATT_VAL_FILE (__builtin_strrchr(__FILE__, '/') ? \
__builtin_strrchr (__FILE__, '/') + 1 : __FILE__)

View File

@@ -26,7 +26,7 @@
class NimBLEValueAttribute {
public:
NimBLEValueAttribute(uint16_t maxLen = BLE_ATT_ATTR_MAX_LEN, uint16_t initLen = MYNEWT_VAL(NIMBLE_CPP_ATT_VALUE_INIT_LENGTH))
NimBLEValueAttribute(uint16_t maxLen = BLE_ATT_ATTR_MAX_LEN, uint16_t initLen = CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH)
: m_value(initLen, maxLen) {}
/**