mirror of
https://github.com/h2zero/esp-nimble-cpp.git
synced 2025-12-23 15:18:16 +01:00
Compare commits
20 Commits
stream-cla
...
2.3.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f80605aff8 | ||
|
|
e26c7406fb | ||
|
|
a547f2529a | ||
|
|
ad145ad503 | ||
|
|
ecc617f9eb | ||
|
|
485a01b78c | ||
|
|
f0ca3bf35d | ||
|
|
ac55482b18 | ||
|
|
8b9e430e5b | ||
|
|
6da9905235 | ||
|
|
18cc463c2c | ||
|
|
e7a1462a99 | ||
|
|
9e141c9f58 | ||
|
|
e5dbd26693 | ||
|
|
2272e3c4a7 | ||
|
|
8c6a9d4258 | ||
|
|
e4d202f1ce | ||
|
|
20349d9e8b | ||
|
|
96c142034e | ||
|
|
252b4f5644 |
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@@ -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
|
||||
|
||||
27
.github/workflows/release.yml
vendored
27
.github/workflows/release.yml
vendored
@@ -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
1
.gitignore
vendored
@@ -1,2 +1 @@
|
||||
docs/doxydocs
|
||||
dist
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
/**
|
||||
|
||||
@@ -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{};
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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, ¬ificationValue, 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
|
||||
|
||||
/**
|
||||
|
||||
@@ -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__)
|
||||
|
||||
@@ -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) {}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user