Update ArduinoJSON, esp-nimble-cpp, Arduino Core, ESP-IDF (#448)

* ArduinoJSON 7.1.0

* Update nimble and arduino core

* Update nuki_ble
This commit is contained in:
iranl
2024-08-11 11:20:31 +02:00
committed by GitHub
parent 4af90cbc79
commit 9d55c2173d
216 changed files with 6437 additions and 5705 deletions

View File

@@ -0,0 +1,42 @@
BasedOnStyle: Google
Language: Cpp
ColumnLimit: 120
IndentWidth: 4
TabWidth: 4
UseTab: Never
SortIncludes: Never
PPIndentWidth : 1
IndentPPDirectives: AfterHash
ReflowComments: true
SpacesBeforeTrailingComments: 1
AlignTrailingComments: true
AlignAfterOpenBracket: Align
AlignConsecutiveMacros:
Enabled: true
AcrossEmptyLines: true
AcrossComments: true
AlignConsecutiveAssignments:
Enabled: true
AcrossEmptyLines: false
AcrossComments: true
AlignCompound: true
PadOperators: true
AlignConsecutiveDeclarations:
Enabled: true
AcrossEmptyLines: false
AcrossComments: true
AlignEscapedNewlines: Left
AccessModifierOffset: -2
AlignArrayOfStructures: Right
AlignOperands: Align
AllowAllArgumentsOnNextLine: false
AllowShortFunctionsOnASingleLine: Inline
AllowShortBlocksOnASingleLine: Empty
BreakBeforeBinaryOperators: None
BinPackArguments: false
BinPackParameters: false
DerivePointerAlignment: false
PenaltyBreakAssignment: 4
PenaltyExcessCharacter: 4
PenaltyBreakBeforeFirstCallParameter: 1
PointerAlignment: Left

View File

@@ -4,6 +4,8 @@ on:
workflow_dispatch: # Start a workflow
pull_request:
push:
branches:
- master
jobs:
build-esp-idf-component:
@@ -16,7 +18,7 @@ jobs:
# https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/tools/idf-docker-image.html
# for details.
idf_ver: ["release-v4.4", "release-v5.1"]
idf_target: ["esp32", "esp32s3", "esp32c2", "esp32c3", "esp32c6"]
idf_target: ["esp32", "esp32s3", "esp32c2", "esp32c3", "esp32c6", "esp32h2"]
example:
- Advanced/NimBLE_Client
- Advanced/NimBLE_Server
@@ -28,6 +30,7 @@ jobs:
- Bluetooth_5/NimBLE_extended_client
- Bluetooth_5/NimBLE_extended_server
- Bluetooth_5/NimBLE_multi_advertiser
- NimBLE_server_get_client_name
exclude:
- idf_target: "esp32"
example: Bluetooth_5/NimBLE_extended_client
@@ -39,11 +42,13 @@ jobs:
idf_target: "esp32c2"
- idf_ver: release-v4.4
idf_target: "esp32c6"
- idf_ver: release-v4.4
idf_target: "esp32h2"
container: espressif/idf:${{ matrix.idf_ver }}
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
path: components/esp-nimble-cpp
- name: Build examples
@@ -58,7 +63,7 @@ jobs:
build_docs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Doxygen Action
uses: mattnotmitt/doxygen-action@v1.9.5
with:

View File

@@ -31,6 +31,7 @@ idf_component_register(
"esp32c2"
"esp32c3"
"esp32c6"
"esp32h2"
INCLUDE_DIRS
"src"
SRCS
@@ -38,6 +39,7 @@ idf_component_register(
"src/NimBLEAddress.cpp"
"src/NimBLEAdvertisedDevice.cpp"
"src/NimBLEAdvertising.cpp"
"src/NimBLEAttValue.cpp"
"src/NimBLEBeacon.cpp"
"src/NimBLECharacteristic.cpp"
"src/NimBLEClient.cpp"
@@ -50,6 +52,7 @@ idf_component_register(
"src/NimBLERemoteCharacteristic.cpp"
"src/NimBLERemoteDescriptor.cpp"
"src/NimBLERemoteService.cpp"
"src/NimBLERemoteValueAttribute.cpp"
"src/NimBLEScan.cpp"
"src/NimBLEServer.cpp"
"src/NimBLEService.cpp"

View File

@@ -33,7 +33,7 @@ config NIMBLE_CPP_ENABLE_RETURN_CODE_TEXT
Enabling this option will display return code values as text
messages in the debug log. This will use approximately 8kB
of flash memory.
config NIMBLE_CPP_ENABLE_GAP_EVENT_CODE_TEXT
bool "Show NimBLE gap events as text in debug log."
default "n"
@@ -47,7 +47,7 @@ config NIMBLE_CPP_ENABLE_ADVERTISEMENT_TYPE_TEXT
default "n"
help
Enabling this option will display advertisment types recieved
while scanning as text messages in the debug log.
while scanning as text messages in the debug log.
This will use approximately 250 bytes of flash memory.
config NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED
@@ -68,5 +68,12 @@ config NIMBLE_CPP_ATT_VALUE_INIT_LENGTH
when the constructor is called. This is also the size used when a remote
characteristic or descriptor is constructed before a value is read/notifed.
Increasing this will reduce reallocations but increase memory footprint.
config NIMBLE_CPP_DEBUG_ASSERT_ENABLED
bool "Enable debug asserts."
default "n"
help
Enabling this option will add debug asserts to the NimBLE CPP library.
This will use approximately 1kB of flash memory.
endmenu

View File

@@ -92,7 +92,7 @@ class scanCallbacks: public NimBLEScanCallbacks {
void notifyCB(NimBLERemoteCharacteristic* pRemoteCharacteristic, uint8_t* pData, size_t length, bool isNotify){
std::string str = (isNotify == true) ? "Notification" : "Indication";
str += " from ";
str += pRemoteCharacteristic->getRemoteService()->getClient()->getPeerAddress().toString();
str += pRemoteCharacteristic->getClient()->getPeerAddress().toString();
str += ": Service = " + pRemoteCharacteristic->getRemoteService()->getUUID().toString();
str += ", Characteristic = " + pRemoteCharacteristic->getUUID().toString();
str += ", Value = " + std::string((char*)pData, length);
@@ -109,7 +109,7 @@ bool connectToServer() {
NimBLEClient* pClient = nullptr;
/** Check if we have a client we should reuse first **/
if(NimBLEDevice::getClientListSize()) {
if(NimBLEDevice::getCreatedClientCount()) {
/** Special case when we already know this device, we send false as the
* second argument in connect() to prevent refreshing the service database.
* This saves considerable time and power.
@@ -132,7 +132,7 @@ bool connectToServer() {
/** No client to reuse? Create a new one. */
if(!pClient) {
if(NimBLEDevice::getClientListSize() >= NIMBLE_MAX_CONNECTIONS) {
if(NimBLEDevice::getCreatedClientCount() >= NIMBLE_MAX_CONNECTIONS) {
printf("Max clients reached - no more connections available\n");
return false;
}

View File

@@ -142,7 +142,7 @@ void notifyTask(void * parameter){
if(pSvc) {
NimBLECharacteristic* pChr = pSvc->getCharacteristic("F00D");
if(pChr) {
pChr->notify(true);
pChr->notify();
}
}
}

View File

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

View File

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

View File

@@ -0,0 +1,83 @@
/** NimBLE_server_get_client_name
*
* Demonstrates 2 ways for the server to read the device name from the connected client.
*
* Created: on June 24 2024
* Author: H2zero
*
*/
#include <NimBLEDevice.h>
// See the following for generating UUIDs:
// https://www.uuidgenerator.net/
#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8"
#define ENC_CHARACTERISTIC_UUID "9551f35b-8d91-42e4-8f7e-1358dfe272dc"
NimBLEServer* pServer;
class ServerCallbacks : public NimBLEServerCallbacks {
// Same as before but now includes the name parameter
void onConnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo, std::string& name) override {
printf("Client address: %s Name: %s\n", connInfo.getAddress().toString().c_str(), name.c_str());
}
// Same as before but now includes the name parameter
void onAuthenticationComplete(const NimBLEConnInfo& connInfo, const std::string& name) override {
if (!connInfo.isEncrypted()) {
NimBLEDevice::getServer()->disconnect(connInfo.getConnHandle());
printf("Encrypt connection failed - disconnecting client\n");
return;
}
printf("Encrypted Client address: %s Name: %s\n", connInfo.getAddress().toString().c_str(), name.c_str());
}
};
extern "C" void app_main(void) {
printf("Starting BLE Server!\n");
NimBLEDevice::init("Connect to me!");
NimBLEDevice::setSecurityAuth(true, false, true); // Enable bonding to see full name on phones.
pServer = NimBLEDevice::createServer();
NimBLEService* pService = pServer->createService(SERVICE_UUID);
NimBLECharacteristic* pCharacteristic =
pService->createCharacteristic(CHARACTERISTIC_UUID, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE);
pCharacteristic->setValue("Hello World says NimBLE!");
NimBLECharacteristic* pEncCharacteristic = pService->createCharacteristic(
ENC_CHARACTERISTIC_UUID,
(NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::READ_ENC | NIMBLE_PROPERTY::WRITE_ENC));
pEncCharacteristic->setValue("Hello World says NimBLE Encrypted");
pService->start();
pServer->setCallbacks(new ServerCallbacks());
pServer->getPeerNameOnConnect(true); // Setting this will enable the onConnect callback that provides the name.
BLEAdvertising* pAdvertising = NimBLEDevice::getAdvertising();
pAdvertising->addServiceUUID(SERVICE_UUID);
pAdvertising->setScanResponse(true);
pAdvertising->start();
printf("Advertising started, connect with your phone.\n");
while (true) {
auto clientCount = pServer->getConnectedCount();
if (clientCount) {
printf("Connected clients:\n");
for (auto i = 0; i < clientCount; ++i) {
NimBLEConnInfo peerInfo = pServer->getPeerInfo(i);
printf("Client address: %s Name: %s\n", peerInfo.getAddress().toString().c_str(),
// This function blocks until the name is retrieved, so cannot be used in callback functions.
pServer->getPeerName(peerInfo).c_str());
}
}
vTaskDelay(pdMS_TO_TICKS(10000));
}
}

View File

@@ -0,0 +1,12 @@
# Override some defaults so BT stack is enabled
# in this example
#
# BT config
#
CONFIG_BT_ENABLED=y
CONFIG_BTDM_CTRL_MODE_BLE_ONLY=y
CONFIG_BTDM_CTRL_MODE_BR_EDR_ONLY=n
CONFIG_BTDM_CTRL_MODE_BTDM=n
CONFIG_BT_BLUEDROID_ENABLED=n
CONFIG_BT_NIMBLE_ENABLED=y

View File

@@ -14,148 +14,151 @@
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include <algorithm>
# include "NimBLEAddress.h"
# include "NimBLELog.h"
#include "NimBLEAddress.h"
#include "NimBLEUtils.h"
#include "NimBLELog.h"
# include <algorithm>
static const char* LOG_TAG = "NimBLEAddress";
/*************************************************
* NOTE: NimBLE address bytes are in INVERSE ORDER!
* We will accomodate that fact in these methods.
*************************************************/
* We will accommodate that fact in these methods.
*************************************************/
/**
* @brief Create an address from the native NimBLE representation.
* @param [in] address The native NimBLE address.
*/
NimBLEAddress::NimBLEAddress(ble_addr_t address) {
memcpy(m_address, address.val, 6);
m_addrType = address.type;
} // NimBLEAddress
NimBLEAddress::NimBLEAddress(ble_addr_t address) : ble_addr_t{address} {}
/**
* @brief Create a blank address, i.e. 00:00:00:00:00:00, type 0.
*/
NimBLEAddress::NimBLEAddress() {
NimBLEAddress("");
} // NimBLEAddress
/**
* @brief Create an address from a hex string
* @brief Create an address from a hex string.
*
* A hex string is of the format:
* ```
* 00:00:00:00:00:00
* ```
* which is 17 characters in length.
*
* @param [in] stringAddress The hex string representation of the address.
* @param [in] addr The hex string representation of the address.
* @param [in] type The type of the address.
*/
NimBLEAddress::NimBLEAddress(const std::string &stringAddress, uint8_t type) {
m_addrType = type;
NimBLEAddress::NimBLEAddress(const std::string& addr, uint8_t type) {
this->type = type;
if (stringAddress.length() == 0) {
memset(m_address, 0, 6);
if (addr.length() == BLE_DEV_ADDR_LEN) {
std::reverse_copy(addr.data(), addr.data() + BLE_DEV_ADDR_LEN, this->val);
return;
}
if (stringAddress.length() == 6) {
std::reverse_copy(stringAddress.data(), stringAddress.data() + 6, m_address);
if (addr.length() == 17) {
std::string mac{addr};
mac.erase(std::remove(mac.begin(), mac.end(), ':'), mac.end());
uint64_t address = std::stoul(mac, nullptr, 16);
memcpy(this->val, &address, sizeof this->val);
return;
}
if (stringAddress.length() != 17) {
memset(m_address, 0, sizeof m_address); // "00:00:00:00:00:00" represents an invalid address
NIMBLE_LOGD(LOG_TAG, "Invalid address '%s'", stringAddress.c_str());
return;
}
int data[6];
if(sscanf(stringAddress.c_str(), "%x:%x:%x:%x:%x:%x", &data[5], &data[4], &data[3], &data[2], &data[1], &data[0]) != 6) {
memset(m_address, 0, sizeof m_address); // "00:00:00:00:00:00" represents an invalid address
NIMBLE_LOGD(LOG_TAG, "Invalid address '%s'", stringAddress.c_str());
}
for(size_t index = 0; index < sizeof m_address; index++) {
m_address[index] = data[index];
}
*this = NimBLEAddress{};
NIMBLE_LOGE(LOG_TAG, "Invalid address '%s'", addr.c_str());
} // NimBLEAddress
/**
* @brief Constructor for compatibility with bluedroid esp library using native ESP representation.
* @param [in] address A uint8_t[6] or esp_bd_addr_t containing the address.
* @param [in] type The type of the address.
*/
NimBLEAddress::NimBLEAddress(uint8_t address[6], uint8_t type) {
std::reverse_copy(address, address + sizeof m_address, m_address);
m_addrType = type;
NimBLEAddress::NimBLEAddress(const uint8_t address[BLE_DEV_ADDR_LEN], uint8_t type) {
std::reverse_copy(address, address + BLE_DEV_ADDR_LEN, this->val);
this->type = type;
} // NimBLEAddress
/**
* @brief Constructor for address using a hex value.\n
* Use the same byte order, so use 0xa4c1385def16 for "a4:c1:38:5d:ef:16"
* @param [in] address uint64_t containing the address.
* @param [in] type The type of the address.
*/
NimBLEAddress::NimBLEAddress(const uint64_t &address, uint8_t type) {
memcpy(m_address, &address, sizeof m_address);
m_addrType = type;
NimBLEAddress::NimBLEAddress(const uint64_t& address, uint8_t type) {
memcpy(this->val, &address, sizeof this->val);
this->type = type;
} // NimBLEAddress
/**
* @brief Determine if this address equals another.
* @param [in] otherAddress The other address to compare against.
* @return True if the addresses are equal.
*/
bool NimBLEAddress::equals(const NimBLEAddress &otherAddress) const {
bool NimBLEAddress::equals(const NimBLEAddress& otherAddress) const {
return *this == otherAddress;
} // equals
/**
* @brief Get the native representation of the address.
* @return a pointer to the uint8_t[6] array of the address.
* @brief Get the NimBLE base struct of the address.
* @return A read only reference to the NimBLE base struct of the address.
*/
const uint8_t *NimBLEAddress::getNative() const {
return m_address;
} // getNative
const ble_addr_t* NimBLEAddress::getBase() const {
return reinterpret_cast<const ble_addr_t*>(this);
} // getBase
/**
* @brief Get the address type.
* @return The address type.
*/
uint8_t NimBLEAddress::getType() const {
return m_addrType;
return this->type;
} // getType
/**
* @brief Get the address value.
* @return A read only reference to the address value.
*/
const uint8_t* NimBLEAddress::getVal() const {
return this->val;
} // getVal
/**
* @brief Determine if this address is a Resolvable Private Address.
* @return True if the address is a RPA.
*/
bool NimBLEAddress::isRpa() const {
return (m_addrType && ((m_address[5] & 0xc0) == 0x40));
return BLE_ADDR_IS_RPA(this);
} // isRpa
/**
* @brief Determine if this address is a Non-Resolvable Private Address.
* @return True if the address is a NRPA.
*/
bool NimBLEAddress::isNrpa() const {
return BLE_ADDR_IS_NRPA(this);
} // isNrpa
/**
* @brief Determine if this address is a Static Address.
* @return True if the address is a Static Address.
*/
bool NimBLEAddress::isStatic() const {
return BLE_ADDR_IS_STATIC(this);
} // isStatic
/**
* @brief Determine if this address is a Public Address.
* @return True if the address is a Public Address.
*/
bool NimBLEAddress::isPublic() const {
return this->type == BLE_ADDR_PUBLIC;
} // isPublic
/**
* @brief Determine if this address is a NULL Address.
* @return True if the address is a NULL Address.
*/
bool NimBLEAddress::isNull() const {
return *this == NimBLEAddress{};
} // isNull
/**
* @brief Convert a BLE address to a string.
*
* A string representation of an address is in the format:
*
* ```
* xx:xx:xx:xx:xx:xx
* ```
*
* @return The string representation of the address.
* @deprecated Use std::string() operator instead.
*/
@@ -163,43 +166,57 @@ std::string NimBLEAddress::toString() const {
return std::string(*this);
} // toString
/**
* @brief Reverse the byte order of the address.
* @return A reference to this address.
*/
const NimBLEAddress& NimBLEAddress::reverseByteOrder() {
std::reverse(this->val, this->val + BLE_DEV_ADDR_LEN);
return *this;
} // reverseByteOrder
/**
* @brief Convenience operator to check if this address is equal to another.
*/
bool NimBLEAddress::operator ==(const NimBLEAddress & rhs) const {
return memcmp(rhs.m_address, m_address, sizeof m_address) == 0;
} // operator ==
bool NimBLEAddress::operator==(const NimBLEAddress& rhs) const {
if (this->type != rhs.type) {
return false;
}
return memcmp(rhs.val, this->val, sizeof this->val) == 0;
} // operator ==
/**
* @brief Convenience operator to check if this address is not equal to another.
*/
bool NimBLEAddress::operator !=(const NimBLEAddress & rhs) const {
bool NimBLEAddress::operator!=(const NimBLEAddress& rhs) const {
return !this->operator==(rhs);
} // operator !=
/**
* @brief Convienience operator to convert this address to string representation.
* @details This allows passing NimBLEAddress to functions
* that accept std::string and/or or it's methods as a parameter.
* @brief Convenience operator to convert this address to string representation.
* @details This allows passing NimBLEAddress to functions that accept std::string and/or it's methods as a parameter.
*/
NimBLEAddress::operator std::string() const {
char buffer[18];
snprintf(buffer, sizeof(buffer), "%02x:%02x:%02x:%02x:%02x:%02x",
m_address[5], m_address[4], m_address[3],
m_address[2], m_address[1], m_address[0]);
return std::string(buffer);
snprintf(buffer,
sizeof(buffer),
"%02x:%02x:%02x:%02x:%02x:%02x",
this->val[5],
this->val[4],
this->val[3],
this->val[2],
this->val[1],
this->val[0]);
return std::string{buffer};
} // operator std::string
/**
* @brief Convenience operator to convert the native address representation to uint_64.
*/
NimBLEAddress::operator uint64_t() const {
uint64_t address = 0;
memcpy(&address, m_address, sizeof m_address);
memcpy(&address, this->val, sizeof this->val);
return address;
} // operator uint64_t

View File

@@ -12,52 +12,56 @@
* Author: kolban
*/
#ifndef COMPONENTS_NIMBLEADDRESS_H_
#define COMPONENTS_NIMBLEADDRESS_H_
#ifndef NIMBLE_CPP_ADDRESS_H_
#define NIMBLE_CPP_ADDRESS_H_
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED)
#if defined(CONFIG_NIMBLE_CPP_IDF)
#include "nimble/ble.h"
#else
#include "nimble/nimble/include/nimble/ble.h"
#endif
# if defined(CONFIG_NIMBLE_CPP_IDF)
# include "nimble/ble.h"
# else
# include "nimble/nimble/include/nimble/ble.h"
# endif
/**** FIX COMPILATION ****/
#undef min
#undef max
# undef min
# undef max
/**************************/
#include <string>
#include <algorithm>
# include <string>
/**
* @brief A %BLE device address.
*
* Every %BLE device has a unique address which can be used to identify it and form connections.
*/
class NimBLEAddress {
public:
NimBLEAddress();
NimBLEAddress(ble_addr_t address);
NimBLEAddress(uint8_t address[6], uint8_t type = BLE_ADDR_PUBLIC);
NimBLEAddress(const std::string &stringAddress, uint8_t type = BLE_ADDR_PUBLIC);
NimBLEAddress(const uint64_t &address, uint8_t type = BLE_ADDR_PUBLIC);
bool isRpa() const;
bool equals(const NimBLEAddress &otherAddress) const;
const uint8_t* getNative() const;
std::string toString() const;
uint8_t getType() const;
class NimBLEAddress : private ble_addr_t {
public:
/**
* @brief Create a blank address, i.e. 00:00:00:00:00:00, type 0.
*/
NimBLEAddress() = default;
NimBLEAddress(const ble_addr_t address);
NimBLEAddress(const uint8_t address[BLE_DEV_ADDR_LEN], uint8_t type = BLE_ADDR_PUBLIC);
NimBLEAddress(const std::string& stringAddress, uint8_t type = BLE_ADDR_PUBLIC);
NimBLEAddress(const uint64_t& address, uint8_t type = BLE_ADDR_PUBLIC);
bool operator ==(const NimBLEAddress & rhs) const;
bool operator !=(const NimBLEAddress & rhs) const;
operator std::string() const;
operator uint64_t() const;
private:
uint8_t m_address[6];
uint8_t m_addrType;
bool isRpa() const;
bool isNrpa() const;
bool isStatic() const;
bool isPublic() const;
bool isNull() const;
bool equals(const NimBLEAddress& otherAddress) const;
const ble_addr_t* getBase() const;
std::string toString() const;
uint8_t getType() const;
const uint8_t* getVal() const;
const NimBLEAddress& reverseByteOrder();
bool operator==(const NimBLEAddress& rhs) const;
bool operator!=(const NimBLEAddress& rhs) const;
operator std::string() const;
operator uint64_t() const;
};
#endif /* CONFIG_BT_ENABLED */
#endif /* COMPONENTS_NIMBLEADDRESS_H_ */
#endif /* NIMBLE_CPP_ADDRESS_H_ */

File diff suppressed because it is too large Load Diff

View File

@@ -12,25 +12,25 @@
* Author: kolban
*/
#ifndef COMPONENTS_NIMBLEADVERTISEDDEVICE_H_
#define COMPONENTS_NIMBLEADVERTISEDDEVICE_H_
#ifndef NIMBLE_CPP_ADVERTISED_DEVICE_H_
#define NIMBLE_CPP_ADVERTISED_DEVICE_H_
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
#include "NimBLEAddress.h"
#include "NimBLEScan.h"
#include "NimBLEUUID.h"
# include "NimBLEAddress.h"
# include "NimBLEScan.h"
# include "NimBLEUUID.h"
#if defined(CONFIG_NIMBLE_CPP_IDF)
#include "host/ble_hs_adv.h"
#else
#include "nimble/nimble/host/include/host/ble_hs_adv.h"
#endif
#include <map>
#include <vector>
#include <time.h>
# if defined(CONFIG_NIMBLE_CPP_IDF)
# include "host/ble_hs_adv.h"
# include "host/ble_gap.h"
# else
# include "nimble/nimble/host/include/host/ble_hs_adv.h"
# include "nimble/nimble/host/include/host/ble_gap.h"
# endif
# include <vector>
class NimBLEScan;
/**
@@ -40,20 +40,59 @@ class NimBLEScan;
* class provides a model of a detected device.
*/
class NimBLEAdvertisedDevice {
public:
NimBLEAdvertisedDevice();
public:
NimBLEAdvertisedDevice() = default;
NimBLEAddress getAddress();
uint8_t getAdvType();
uint8_t getAdvFlags();
uint16_t getAppearance();
uint16_t getAdvInterval();
uint16_t getMinInterval();
uint16_t getMaxInterval();
uint8_t getManufacturerDataCount();
std::string getManufacturerData(uint8_t index = 0);
std::string getURI();
std::string getPayloadByType(uint16_t type);
uint8_t getAdvType() const;
uint8_t getAdvFlags() const;
uint16_t getAppearance() const;
uint16_t getAdvInterval() const;
uint16_t getMinInterval() const;
uint16_t getMaxInterval() const;
uint8_t getManufacturerDataCount() const;
const NimBLEAddress& getAddress() const;
std::string getManufacturerData(uint8_t index = 0) const;
std::string getURI() const;
std::string getPayloadByType(uint16_t type, uint8_t index = 0) const;
std::string getName() const;
int8_t getRSSI() const;
NimBLEScan* getScan() const;
uint8_t getServiceDataCount() const;
std::string getServiceData(uint8_t index = 0) const;
std::string getServiceData(const NimBLEUUID& uuid) const;
NimBLEUUID getServiceDataUUID(uint8_t index = 0) const;
NimBLEUUID getServiceUUID(uint8_t index = 0) const;
uint8_t getServiceUUIDCount() const;
NimBLEAddress getTargetAddress(uint8_t index = 0) const;
uint8_t getTargetAddressCount() const;
int8_t getTXPower() const;
uint8_t getAdvLength() const;
uint8_t getAddressType() const;
bool isAdvertisingService(const NimBLEUUID& uuid) const;
bool haveAppearance() const;
bool haveManufacturerData() const;
bool haveName() const;
bool haveServiceData() const;
bool haveServiceUUID() const;
bool haveTXPower() const;
bool haveConnParams() const;
bool haveAdvInterval() const;
bool haveTargetAddress() const;
bool haveURI() const;
bool haveType(uint16_t type) const;
std::string toString() const;
bool isConnectable() const;
bool isLegacyAdvertisement() const;
# if CONFIG_BT_NIMBLE_EXT_ADV
uint8_t getSetId() const;
uint8_t getPrimaryPhy() const;
uint8_t getSecondaryPhy() const;
uint16_t getPeriodicInterval() const;
# endif
const std::vector<uint8_t>& getPayload() const;
const std::vector<uint8_t>::const_iterator begin() const;
const std::vector<uint8_t>::const_iterator end() const;
/**
* @brief A template to convert the service data to <type\>.
@@ -63,21 +102,14 @@ public:
* less than <tt>sizeof(<type\>)</tt>.
* @details <b>Use:</b> <tt>getManufacturerData<type>(skipSizeCheck);</tt>
*/
template<typename T>
T getManufacturerData(bool skipSizeCheck = false) {
template <typename T>
T getManufacturerData(bool skipSizeCheck = false) const {
std::string data = getManufacturerData();
if(!skipSizeCheck && data.size() < sizeof(T)) return T();
const char *pData = data.data();
return *((T *)pData);
if (!skipSizeCheck && data.size() < sizeof(T)) return T();
const char* pData = data.data();
return *((T*)pData);
}
std::string getName();
int getRSSI();
NimBLEScan* getScan();
uint8_t getServiceDataCount();
std::string getServiceData(uint8_t index = 0);
std::string getServiceData(const NimBLEUUID &uuid);
/**
* @brief A template to convert the service data to <tt><type\></tt>.
* @tparam T The type to convert the data to.
@@ -87,12 +119,12 @@ public:
* less than <tt>sizeof(<type\>)</tt>.
* @details <b>Use:</b> <tt>getServiceData<type>(skipSizeCheck);</tt>
*/
template<typename T>
T getServiceData(uint8_t index = 0, bool skipSizeCheck = false) {
template <typename T>
T getServiceData(uint8_t index = 0, bool skipSizeCheck = false) const {
std::string data = getServiceData(index);
if(!skipSizeCheck && data.size() < sizeof(T)) return T();
const char *pData = data.data();
return *((T *)pData);
if (!skipSizeCheck && data.size() < sizeof(T)) return T();
const char* pData = data.data();
return *((T*)pData);
}
/**
@@ -104,80 +136,38 @@ public:
* less than <tt>sizeof(<type\>)</tt>.
* @details <b>Use:</b> <tt>getServiceData<type>(skipSizeCheck);</tt>
*/
template<typename T>
T getServiceData(const NimBLEUUID &uuid, bool skipSizeCheck = false) {
template <typename T>
T getServiceData(const NimBLEUUID& uuid, bool skipSizeCheck = false) const {
std::string data = getServiceData(uuid);
if(!skipSizeCheck && data.size() < sizeof(T)) return T();
const char *pData = data.data();
return *((T *)pData);
if (!skipSizeCheck && data.size() < sizeof(T)) return T();
const char* pData = data.data();
return *((T*)pData);
}
NimBLEUUID getServiceDataUUID(uint8_t index = 0);
NimBLEUUID getServiceUUID(uint8_t index = 0);
uint8_t getServiceUUIDCount();
NimBLEAddress getTargetAddress(uint8_t index = 0);
uint8_t getTargetAddressCount();
int8_t getTXPower();
uint8_t* getPayload();
uint8_t getAdvLength();
size_t getPayloadLength();
uint8_t getAddressType();
time_t getTimestamp();
bool isAdvertisingService(const NimBLEUUID &uuid);
bool haveAppearance();
bool haveManufacturerData();
bool haveName();
bool haveRSSI();
bool haveServiceData();
bool haveServiceUUID();
bool haveTXPower();
bool haveConnParams();
bool haveAdvInterval();
bool haveTargetAddress();
bool haveURI();
bool haveType(uint16_t type);
std::string toString();
bool isConnectable();
bool isLegacyAdvertisement();
#if CONFIG_BT_NIMBLE_EXT_ADV
uint8_t getSetId();
uint8_t getPrimaryPhy();
uint8_t getSecondaryPhy();
uint16_t getPeriodicInterval();
#endif
private:
private:
friend class NimBLEScan;
void setAddress(NimBLEAddress address);
void setAdvType(uint8_t advType, bool isLegacyAdv);
void setPayload(const uint8_t *payload, uint8_t length, bool append);
void setRSSI(int rssi);
#if CONFIG_BT_NIMBLE_EXT_ADV
void setSetId(uint8_t sid) { m_sid = sid; }
void setPrimaryPhy(uint8_t phy) { m_primPhy = phy; }
void setSecondaryPhy(uint8_t phy) { m_secPhy = phy; }
void setPeriodicInterval(uint16_t itvl) { m_periodicItvl = itvl; }
#endif
uint8_t findAdvField(uint8_t type, uint8_t index = 0, size_t * data_loc = nullptr);
size_t findServiceData(uint8_t index, uint8_t* bytes);
NimBLEAdvertisedDevice(const ble_gap_event* event, uint8_t eventType);
void update(const ble_gap_event* event, uint8_t eventType);
uint8_t findAdvField(uint8_t type, uint8_t index = 0, size_t* data_loc = nullptr) const;
size_t findServiceData(uint8_t index, uint8_t* bytes) const;
NimBLEAddress m_address = NimBLEAddress("");
uint8_t m_advType;
int m_rssi;
time_t m_timestamp;
uint8_t m_callbackSent;
uint8_t m_advLength;
#if CONFIG_BT_NIMBLE_EXT_ADV
bool m_isLegacyAdv;
uint8_t m_sid;
uint8_t m_primPhy;
uint8_t m_secPhy;
uint16_t m_periodicItvl;
#endif
NimBLEAddress m_address{};
uint8_t m_advType{};
int8_t m_rssi{};
uint8_t m_callbackSent{};
uint8_t m_advLength{};
std::vector<uint8_t> m_payload;
# if CONFIG_BT_NIMBLE_EXT_ADV
bool m_isLegacyAdv{};
uint8_t m_sid{};
uint8_t m_primPhy{};
uint8_t m_secPhy{};
uint16_t m_periodicItvl{};
# endif
std::vector<uint8_t> m_payload;
};
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_OBSERVER */
#endif /* COMPONENTS_NIMBLEADVERTISEDDEVICE_H_ */
#endif /* NIMBLE_CPP_ADVERTISED_DEVICE_H_ */

View File

@@ -196,8 +196,7 @@ void NimBLEAdvertising::setURI(const std::string &uri) {
void NimBLEAdvertising::setServiceData(const NimBLEUUID &uuid, const std::string &data) {
switch (uuid.bitSize()) {
case 16: {
std::vector<uint8_t>((uint8_t*)&uuid.getNative()->u16.value,
(uint8_t*)&uuid.getNative()->u16.value + 2).swap(m_svcData16);
std::vector<uint8_t>(uuid.getValue(), uuid.getValue() + 2).swap(m_svcData16);
m_svcData16.insert(m_svcData16.end(), data.begin(), data.end());
m_advData.svc_data_uuid16 = (uint8_t*)&m_svcData16[0];
m_advData.svc_data_uuid16_len = (data.length() > 0) ? m_svcData16.size() : 0;
@@ -205,8 +204,7 @@ void NimBLEAdvertising::setServiceData(const NimBLEUUID &uuid, const std::string
}
case 32: {
std::vector<uint8_t>((uint8_t*)&uuid.getNative()->u32.value,
(uint8_t*)&uuid.getNative()->u32.value + 4).swap(m_svcData32);
std::vector<uint8_t>(uuid.getValue(), uuid.getValue() + 4).swap(m_svcData32);
m_svcData32.insert(m_svcData32.end(), data.begin(), data.end());
m_advData.svc_data_uuid32 = (uint8_t*)&m_svcData32[0];
m_advData.svc_data_uuid32_len = (data.length() > 0) ? m_svcData32.size() : 0;
@@ -214,8 +212,7 @@ void NimBLEAdvertising::setServiceData(const NimBLEUUID &uuid, const std::string
}
case 128: {
std::vector<uint8_t>(uuid.getNative()->u128.value,
uuid.getNative()->u128.value + 16).swap(m_svcData128);
std::vector<uint8_t>(uuid.getValue(), uuid.getValue() + 16).swap(m_svcData128);
m_svcData128.insert(m_svcData128.end(), data.begin(), data.end());
m_advData.svc_data_uuid128 = (uint8_t*)&m_svcData128[0];
m_advData.svc_data_uuid128_len = (data.length() > 0) ? m_svcData128.size() : 0;
@@ -489,7 +486,7 @@ bool NimBLEAdvertising::start(uint32_t duration, advCompleteCB_t advCompleteCB,
payloadLen += (2 + BLE_HS_ADV_SLAVE_ITVL_RANGE_LEN);
for(auto &it : m_serviceUUIDs) {
if(it.getNative()->u.type == BLE_UUID_TYPE_16) {
if(it.bitSize() == BLE_UUID_TYPE_16) {
int add = (m_advData.num_uuids16 > 0) ? 2 : 4;
if((payloadLen + add) > BLE_HS_ADV_MAX_SZ){
m_advData.uuids16_is_complete = 0;
@@ -497,18 +494,17 @@ bool NimBLEAdvertising::start(uint32_t duration, advCompleteCB_t advCompleteCB,
}
payloadLen += add;
if(nullptr == (m_advData.uuids16 = (ble_uuid16_t*)realloc((void*)m_advData.uuids16,
(m_advData.num_uuids16 + 1) * sizeof(ble_uuid16_t))))
if(nullptr == (m_advData.uuids16 = reinterpret_cast<ble_uuid16_t*>(realloc((void*)m_advData.uuids16,
(m_advData.num_uuids16 + 1) * sizeof(ble_uuid16_t)))))
{
NIMBLE_LOGC(LOG_TAG, "Error, no mem");
abort();
NIMBLE_LOGE(LOG_TAG, "Error, no mem");
return false;
}
memcpy((void*)&m_advData.uuids16[m_advData.num_uuids16],
&it.getNative()->u16, sizeof(ble_uuid16_t));
reinterpret_cast<ble_uuid16_t*>(&it), sizeof(ble_uuid16_t));
m_advData.uuids16_is_complete = 1;
m_advData.num_uuids16++;
}
if(it.getNative()->u.type == BLE_UUID_TYPE_32) {
} else if(it.bitSize() == BLE_UUID_TYPE_32) {
int add = (m_advData.num_uuids32 > 0) ? 4 : 6;
if((payloadLen + add) > BLE_HS_ADV_MAX_SZ){
m_advData.uuids32_is_complete = 0;
@@ -516,18 +512,17 @@ bool NimBLEAdvertising::start(uint32_t duration, advCompleteCB_t advCompleteCB,
}
payloadLen += add;
if(nullptr == (m_advData.uuids32 = (ble_uuid32_t*)realloc((void*)m_advData.uuids32,
(m_advData.num_uuids32 + 1) * sizeof(ble_uuid32_t))))
if(nullptr == (m_advData.uuids32 = reinterpret_cast<ble_uuid32_t*>(realloc((void*)m_advData.uuids32,
(m_advData.num_uuids32 + 1) * sizeof(ble_uuid32_t)))))
{
NIMBLE_LOGC(LOG_TAG, "Error, no mem");
abort();
NIMBLE_LOGE(LOG_TAG, "Error, no mem");
return false;
}
memcpy((void*)&m_advData.uuids32[m_advData.num_uuids32],
&it.getNative()->u32, sizeof(ble_uuid32_t));
reinterpret_cast<ble_uuid32_t*>(&it), sizeof(ble_uuid32_t));
m_advData.uuids32_is_complete = 1;
m_advData.num_uuids32++;
}
if(it.getNative()->u.type == BLE_UUID_TYPE_128){
} else if(it.bitSize() == BLE_UUID_TYPE_128){
int add = (m_advData.num_uuids128 > 0) ? 16 : 18;
if((payloadLen + add) > BLE_HS_ADV_MAX_SZ){
m_advData.uuids128_is_complete = 0;
@@ -535,14 +530,14 @@ bool NimBLEAdvertising::start(uint32_t duration, advCompleteCB_t advCompleteCB,
}
payloadLen += add;
if(nullptr == (m_advData.uuids128 = (ble_uuid128_t*)realloc((void*)m_advData.uuids128,
(m_advData.num_uuids128 + 1) * sizeof(ble_uuid128_t))))
if(nullptr == (m_advData.uuids128 = reinterpret_cast<ble_uuid128_t*>(realloc((void*)m_advData.uuids128,
(m_advData.num_uuids128 + 1) * sizeof(ble_uuid128_t)))))
{
NIMBLE_LOGC(LOG_TAG, "Error, no mem");
abort();
NIMBLE_LOGE(LOG_TAG, "Error, no mem");
return false;
}
memcpy((void*)&m_advData.uuids128[m_advData.num_uuids128],
&it.getNative()->u128, sizeof(ble_uuid128_t));
reinterpret_cast<ble_uuid128_t*>(&it), sizeof(ble_uuid128_t));
m_advData.uuids128_is_complete = 1;
m_advData.num_uuids128++;
}
@@ -643,15 +638,9 @@ bool NimBLEAdvertising::start(uint32_t duration, advCompleteCB_t advCompleteCB,
m_advDataSet = true;
}
ble_addr_t peerAddr;
if (dirAddr != nullptr) {
memcpy(&peerAddr.val, dirAddr->getNative(), 6);
peerAddr.type = dirAddr->getType();
}
#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
rc = ble_gap_adv_start(NimBLEDevice::m_own_addr_type,
(dirAddr != nullptr) ? &peerAddr : NULL,
(dirAddr != nullptr) ? dirAddr->getBase() : NULL,
duration,
&m_advParams,
(pServer != nullptr) ? NimBLEServer::handleGapEvent :
@@ -772,7 +761,7 @@ int NimBLEAdvertising::handleGapEvent(struct ble_gap_event *event, void *arg) {
case BLE_HS_EOS:
case BLE_HS_ECONTROLLER:
case BLE_HS_ENOTSYNCED:
NIMBLE_LOGC(LOG_TAG, "host reset, rc=%d", event->adv_complete.reason);
NIMBLE_LOGE(LOG_TAG, "host reset, rc=%d", event->adv_complete.reason);
NimBLEDevice::onReset(event->adv_complete.reason);
return 0;
default:
@@ -967,21 +956,9 @@ void NimBLEAdvertisementData::setServices(const bool complete, const uint8_t siz
for(auto &it : v_uuid){
if(it.bitSize() != size) {
NIMBLE_LOGE(LOG_TAG, "Service UUID(%d) invalid", size);
return;
continue;
} else {
switch(size) {
case 16:
uuids += std::string((char*)&it.getNative()->u16.value, 2);
break;
case 32:
uuids += std::string((char*)&it.getNative()->u32.value, 4);
break;
case 128:
uuids += std::string((char*)&it.getNative()->u128.value, 16);
break;
default:
return;
}
uuids += std::string(reinterpret_cast<const char*>(it.getValue()), size / 8);
}
}
@@ -995,35 +972,30 @@ void NimBLEAdvertisementData::setServices(const bool complete, const uint8_t siz
* @param [in] data The data to be associated with the service data advertised.
*/
void NimBLEAdvertisementData::setServiceData(const NimBLEUUID &uuid, const std::string &data) {
char cdata[2];
switch (uuid.bitSize()) {
case 16: {
uint8_t size = uuid.bitSize() / 8;
char cdata[2] = {static_cast<char>(1 + size), BLE_HS_ADV_TYPE_SVC_DATA_UUID16};
switch (size) {
case 2: {
// [Len] [0x16] [UUID16] data
cdata[0] = data.length() + 3;
cdata[1] = BLE_HS_ADV_TYPE_SVC_DATA_UUID16; // 0x16
addData(std::string(cdata, 2) + std::string((char*) &uuid.getNative()->u16.value, 2) + data);
break;
}
case 32: {
// [Len] [0x20] [UUID32] data
cdata[0] = data.length() + 5;
cdata[1] = BLE_HS_ADV_TYPE_SVC_DATA_UUID32; // 0x20
addData(std::string(cdata, 2) + std::string((char*) &uuid.getNative()->u32.value, 4) + data);
break;
}
case 128: {
case 16: {
// [Len] [0x21] [UUID128] data
cdata[0] = data.length() + 17;
cdata[1] = BLE_HS_ADV_TYPE_SVC_DATA_UUID128; // 0x21
addData(std::string(cdata, 2) + std::string((char*) &uuid.getNative()->u128.value, 16) + data);
break;
}
case 4: {
// [Len] [0x20] [UUID32] data
cdata[1] = BLE_HS_ADV_TYPE_SVC_DATA_UUID32; // 0x20
break;
}
default:
return;
}
addData(std::string(cdata, 2) + std::string(reinterpret_cast<const char*>(uuid.getValue()), size) + data);
} // setServiceData

View File

@@ -0,0 +1,130 @@
/*
* NimBLEAttValue.cpp
*
* Created: on July 17, 2024
* Author H2zero
*
*/
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED)
# if defined(CONFIG_NIMBLE_CPP_IDF)
# include "nimble/nimble_npl.h"
# else
# include "nimble/nimble/include/nimble/nimble_npl.h"
# endif
# include "NimBLEAttValue.h"
// Default constructor implementation.
NimBLEAttValue::NimBLEAttValue(uint16_t init_len, uint16_t max_len)
: m_attr_value{static_cast<uint8_t*>(calloc(init_len + 1, 1))},
m_attr_max_len{std::min<uint16_t>(BLE_ATT_ATTR_MAX_LEN, max_len)},
m_attr_len{},
m_capacity{init_len}
# if CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED
,
m_timestamp{}
# endif
{
NIMBLE_CPP_DEBUG_ASSERT(m_attr_value);
}
// Value constructor implementation.
NimBLEAttValue::NimBLEAttValue(const uint8_t *value, uint16_t len, uint16_t max_len)
: NimBLEAttValue(len, max_len) {
memcpy(m_attr_value, value, len);
m_attr_value[len] = '\0';
m_attr_len = len;
}
// Destructor implementation.
NimBLEAttValue::~NimBLEAttValue() {
if (m_attr_value != nullptr) {
free(m_attr_value);
}
}
// Move assignment operator implementation.
NimBLEAttValue& NimBLEAttValue::operator=(NimBLEAttValue&& source) {
if (this != &source) {
free(m_attr_value);
m_attr_value = source.m_attr_value;
m_attr_max_len = source.m_attr_max_len;
m_attr_len = source.m_attr_len;
m_capacity = source.m_capacity;
setTimeStamp(source.getTimeStamp());
source.m_attr_value = nullptr;
}
return *this;
}
// Copy assignment implementation.
NimBLEAttValue& NimBLEAttValue::operator=(const NimBLEAttValue& source) {
if (this != &source) {
deepCopy(source);
}
return *this;
}
// Copy all the data from the source object to this object, including allocated space.
void NimBLEAttValue::deepCopy(const NimBLEAttValue& source) {
uint8_t* res = static_cast<uint8_t*>(realloc(m_attr_value, source.m_capacity + 1));
NIMBLE_CPP_DEBUG_ASSERT(res);
ble_npl_hw_enter_critical();
m_attr_value = res;
m_attr_max_len = source.m_attr_max_len;
m_attr_len = source.m_attr_len;
m_capacity = source.m_capacity;
setTimeStamp(source.getTimeStamp());
memcpy(m_attr_value, source.m_attr_value, m_attr_len + 1);
ble_npl_hw_exit_critical(0);
}
// Set the value of the attribute.
bool NimBLEAttValue::setValue(const uint8_t* value, uint16_t len) {
m_attr_len = 0; // Just set the value length to 0 and append instead of repeating code.
append(value, len);
return memcmp(m_attr_value, value, len) == 0 && m_attr_len == len;
}
// Append the new data, allocate as necessary.
NimBLEAttValue& NimBLEAttValue::append(const uint8_t* value, uint16_t len) {
if (len == 0) {
return *this;
}
if ((m_attr_len + len) > m_attr_max_len) {
NIMBLE_LOGE("NimBLEAttValue", "val > max, len=%u, max=%u", len, m_attr_max_len);
return *this;
}
uint8_t* res = m_attr_value;
uint16_t new_len = m_attr_len + len;
if (new_len > m_capacity) {
res = static_cast<uint8_t*>(realloc(m_attr_value, (new_len + 1)));
m_capacity = new_len;
}
NIMBLE_CPP_DEBUG_ASSERT(res);
# if CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED
time_t t = time(nullptr);
# else
time_t t = 0;
# endif
ble_npl_hw_enter_critical();
memcpy(res + m_attr_len, value, len);
m_attr_value = res;
m_attr_len = new_len;
m_attr_value[m_attr_len] = '\0';
setTimeStamp(t);
ble_npl_hw_exit_critical(0);
return *this;
}
#endif // CONFIG_BT_ENABLED

View File

@@ -6,51 +6,45 @@
*
*/
#ifndef MAIN_NIMBLEATTVALUE_H_
#define MAIN_NIMBLEATTVALUE_H_
#ifndef NIMBLE_CPP_ATTVALUE_H
#define NIMBLE_CPP_ATTVALUE_H
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED)
#ifdef NIMBLE_CPP_ARDUINO_STRING_AVAILABLE
#include <Arduino.h>
#endif
# ifdef NIMBLE_CPP_ARDUINO_STRING_AVAILABLE
# include <Arduino.h>
# endif
#include "NimBLELog.h"
# include "NimBLELog.h"
# include <string>
# include <vector>
# include <ctime>
# include <cstring>
# include <cstdint>
/**** FIX COMPILATION ****/
#undef min
#undef max
/**************************/
# ifndef CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED
# define CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED 0
# endif
#include <string>
#include <vector>
#include <ctime>
#ifndef CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED
# define CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED 0
#endif
#if CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED
# include <time.h>
#endif
#if !defined(CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH)
# define CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH 20
#elif CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH > BLE_ATT_ATTR_MAX_LEN
# error CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH cannot be larger than 512 (BLE_ATT_ATTR_MAX_LEN)
#elif CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH < 1
# error CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH cannot be less than 1; Range = 1 : 512
#endif
# ifndef BLE_ATT_ATTR_MAX_LEN
# define BLE_ATT_ATTR_MAX_LEN 512
# endif
# if !defined(CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH)
# define CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH 20
# elif CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH > BLE_ATT_ATTR_MAX_LEN
# error CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH cannot be larger than 512 (BLE_ATT_ATTR_MAX_LEN)
# elif CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH < 1
# error CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH cannot be less than 1; Range = 1 : 512
# endif
/* Used to determine if the type passed to a template has a c_str() and length() method. */
template <typename T, typename = void, typename = void>
struct Has_c_str_len : std::false_type {};
template <typename T>
struct Has_c_str_len<T, decltype(void(std::declval<T &>().c_str())),
decltype(void(std::declval<T &>().length()))> : std::true_type {};
struct Has_c_str_len<T, decltype(void(std::declval<T&>().c_str())), decltype(void(std::declval<T&>().length()))>
: std::true_type {};
/**
* @brief A specialized container class to hold BLE attribute values.
@@ -58,25 +52,23 @@ struct Has_c_str_len<T, decltype(void(std::declval<T &>().c_str())),
* standard container types for value storage, while being convertible to\n
* many different container classes.
*/
class NimBLEAttValue
{
uint8_t* m_attr_value = nullptr;
uint16_t m_attr_max_len = 0;
uint16_t m_attr_len = 0;
uint16_t m_capacity = 0;
#if CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED
time_t m_timestamp = 0;
#endif
void deepCopy(const NimBLEAttValue & source);
class NimBLEAttValue {
uint8_t* m_attr_value{};
uint16_t m_attr_max_len{};
uint16_t m_attr_len{};
uint16_t m_capacity{};
# if CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED
time_t m_timestamp{};
# endif
void deepCopy(const NimBLEAttValue& source);
public:
public:
/**
* @brief Default constructor.
* @param[in] init_len The initial size in bytes.
* @param[in] max_len The max size in bytes that the value can be.
*/
NimBLEAttValue(uint16_t init_len = CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH,
uint16_t max_len = BLE_ATT_ATTR_MAX_LEN);
NimBLEAttValue(uint16_t init_len = 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.
@@ -87,15 +79,6 @@ public:
NimBLEAttValue(const uint8_t *value, uint16_t len,
uint16_t max_len = BLE_ATT_ATTR_MAX_LEN);
/**
* @brief Construct with an initializer list.
* @param list An initializer list containing the initial value to set.
* @param[in] max_len The max size in bytes that the value can be.
*/
NimBLEAttValue(std::initializer_list<uint8_t> list,
uint16_t max_len = BLE_ATT_ATTR_MAX_LEN)
:NimBLEAttValue(list.begin(), (uint16_t)list.size(), max_len){}
/**
* @brief Construct with an initial value from a const char string.
* @param value A pointer to the initial value to set.
@@ -104,13 +87,21 @@ public:
NimBLEAttValue(const char *value, uint16_t max_len = BLE_ATT_ATTR_MAX_LEN)
:NimBLEAttValue((uint8_t*)value, (uint16_t)strlen(value), max_len){}
/**
* @brief Construct with an initializer list.
* @param list An initializer list containing the initial value to set.
* @param[in] max_len The max size in bytes that the value can be.
*/
NimBLEAttValue(std::initializer_list<uint8_t> list, uint16_t max_len = BLE_ATT_ATTR_MAX_LEN)
: NimBLEAttValue(list.begin(), list.size(), max_len) {}
/**
* @brief Construct with an initial value from a std::string.
* @param str A std::string containing to the initial value to set.
* @param[in] max_len The max size in bytes that the value can be.
*/
NimBLEAttValue(const std::string str, uint16_t max_len = BLE_ATT_ATTR_MAX_LEN)
:NimBLEAttValue((uint8_t*)str.data(), (uint16_t)str.length(), max_len){}
: NimBLEAttValue(reinterpret_cast<const uint8_t*>(&str[0]), str.length(), max_len) {}
/**
* @brief Construct with an initial value from a std::vector<uint8_t>.
@@ -118,90 +109,99 @@ public:
* @param[in] max_len The max size in bytes that the value can be.
*/
NimBLEAttValue(const std::vector<uint8_t> vec, uint16_t max_len = BLE_ATT_ATTR_MAX_LEN)
:NimBLEAttValue(&vec[0], (uint16_t)vec.size(), max_len){}
: NimBLEAttValue(&vec[0], vec.size(), max_len) {}
#ifdef NIMBLE_CPP_ARDUINO_STRING_AVAILABLE
# ifdef NIMBLE_CPP_ARDUINO_STRING_AVAILABLE
/**
* @brief Construct with an initial value from an Arduino String.
* @param str An Arduino String containing to the initial value to set.
* @param[in] max_len The max size in bytes that the value can be.
*/
NimBLEAttValue(const String str, uint16_t max_len = BLE_ATT_ATTR_MAX_LEN)
:NimBLEAttValue((uint8_t*)str.c_str(), str.length(), max_len){}
#endif
: NimBLEAttValue(reinterpret_cast<const uint8_t*>(str.c_str()), str.length(), max_len) {}
# endif
/** @brief Copy constructor */
NimBLEAttValue(const NimBLEAttValue & source) { deepCopy(source); }
NimBLEAttValue(const NimBLEAttValue& source) { deepCopy(source); }
/** @brief Move constructor */
NimBLEAttValue(NimBLEAttValue && source) { *this = std::move(source); }
NimBLEAttValue(NimBLEAttValue&& source) { *this = std::move(source); }
/** @brief Destructor */
~NimBLEAttValue();
/** @brief Returns the max size in bytes */
uint16_t max_size() const { return m_attr_max_len; }
uint16_t max_size() const { return m_attr_max_len; }
/** @brief Returns the currently allocated capacity in bytes */
uint16_t capacity() const { return m_capacity; }
uint16_t capacity() const { return m_capacity; }
/** @brief Returns the current length of the value in bytes */
uint16_t length() const { return m_attr_len; }
uint16_t length() const { return m_attr_len; }
/** @brief Returns the current size of the value in bytes */
uint16_t size() const { return m_attr_len; }
uint16_t size() const { return m_attr_len; }
/** @brief Returns a pointer to the internal buffer of the value */
const uint8_t* data() const { return m_attr_value; }
const uint8_t* data() const { return m_attr_value; }
/** @brief Returns a pointer to the internal buffer of the value as a const char* */
const char* c_str() const { return (const char*)m_attr_value; }
const char* c_str() const { return reinterpret_cast<const char*>(m_attr_value); }
/** @brief Iterator begin */
const uint8_t* begin() const { return m_attr_value; }
const uint8_t* begin() const { return m_attr_value; }
/** @brief Iterator end */
const uint8_t* end() const { return m_attr_value + m_attr_len; }
const uint8_t* end() const { return m_attr_value + m_attr_len; }
#if CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED
# if 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; }
time_t getTimeStamp() const { return m_timestamp; }
/** @brief Set the timestamp to the current time */
void setTimeStamp() { m_timestamp = time(nullptr); }
void setTimeStamp() { m_timestamp = time(nullptr); }
/**
* @brief Set the timestamp to the specified time
* @param[in] t The timestamp value to set
*/
void setTimeStamp(time_t t) { m_timestamp = t; }
#else
time_t getTimeStamp() const { return 0; }
void setTimeStamp() { }
void setTimeStamp(time_t t) { }
#endif
void setTimeStamp(time_t t) { m_timestamp = t; }
# else
time_t getTimeStamp() const { return 0; }
void setTimeStamp() {}
void setTimeStamp(time_t t) {}
# endif
/**
* @brief Set the value from a buffer
* @param[in] value A ponter to a buffer containing the value.
* @param[in] value A pointer to a buffer containing the value.
* @param[in] len The length of the value in bytes.
* @returns True if successful.
*/
bool setValue(const uint8_t *value, uint16_t len);
bool setValue(const uint8_t* value, uint16_t len);
/**
* @brief Set value to the value of const char*.
* @param [in] s A ponter to a const char value to set.
* @param [in] s A pointer to a const char value to set.
* @param [in] len The length of the value in bytes, defaults to strlen(s).
*/
bool setValue(const char* s) {
return setValue((uint8_t*)s, (uint16_t)strlen(s)); }
bool setValue(const char* s, uint16_t len = 0) {
if (len == 0) {
len = strlen(s);
}
return setValue(reinterpret_cast<const uint8_t*>(s), len);
}
/**
* @brief Get a pointer to the value buffer with timestamp.
* @param[in] timestamp A ponter to a time_t variable to store the timestamp.
* @returns A pointer to the internal value buffer.
*/
const uint8_t* getValue(time_t *timestamp);
const NimBLEAttValue& getValue(time_t* timestamp = nullptr) const {
if (timestamp != nullptr) {
# if CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED
*timestamp = m_timestamp;
# else
*timestamp = 0;
# endif
}
return *this;
}
/**
* @brief Append data to the value.
@@ -209,39 +209,46 @@ public:
* @param[in] len The length of the value to append in bytes.
* @returns A reference to the appended NimBLEAttValue.
*/
NimBLEAttValue& append(const uint8_t *value, uint16_t len);
NimBLEAttValue& append(const uint8_t* value, uint16_t len);
/*********************** Template Functions ************************/
/**
* @brief Template to set value to the value of <type\>val.
* @param [in] s The <type\>value to set.
* @param [in] len The length of the value in bytes, defaults to sizeof(T).
* @details Only used for types without a `c_str()` method.
*/
template<typename T>
#ifdef _DOXYGEN_
template <typename T>
# ifdef _DOXYGEN_
bool
#else
# else
typename std::enable_if<!Has_c_str_len<T>::value, bool>::type
#endif
setValue(const T &s) {
return setValue((uint8_t*)&s, sizeof(T));
# endif
setValue(const T& s, uint16_t len = 0) {
if (len == 0) {
len = sizeof(T);
}
return setValue(reinterpret_cast<const uint8_t*>(&s), len);
}
/**
* @brief Template to set value to the value of <type\>val.
* @param [in] s The <type\>value to set.
* @param [in] len The length of the value in bytes, defaults to string.length().
* @details Only used if the <type\> has a `c_str()` method.
*/
template<typename T>
#ifdef _DOXYGEN_
template <typename T>
# ifdef _DOXYGEN_
bool
#else
# else
typename std::enable_if<Has_c_str_len<T>::value, bool>::type
#endif
setValue(const T & s) {
return setValue((uint8_t*)s.c_str(), (uint16_t)s.length());
# endif
setValue(const T& s, uint16_t len = 0) {
if (len == 0) {
len = s.length();
}
return setValue(reinterpret_cast<const uint8_t*>(s.c_str()), len);
}
/**
@@ -254,195 +261,67 @@ public:
* less than <tt>sizeof(<type\>)</tt>.
* @details <b>Use:</b> <tt>getValue<type>(&timestamp, skipSizeCheck);</tt>
*/
template<typename T>
T getValue(time_t *timestamp = nullptr, bool skipSizeCheck = false) {
if(!skipSizeCheck && size() < sizeof(T)) {
return T();
}
return *((T *)getValue(timestamp));
}
template <typename T>
T getValue(time_t* timestamp = nullptr, bool skipSizeCheck = false) const {
if (!skipSizeCheck && size() < sizeof(T)) {
return T();
}
if (timestamp != nullptr) {
# if CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED
*timestamp = m_timestamp;
# else
*timestamp = 0;
# endif
}
return *(reinterpret_cast<const T*>(m_attr_value));
}
/*********************** Operators ************************/
/** @brief Subscript operator */
uint8_t operator [](int pos) const {
assert(pos < m_attr_len && "out of range"); return m_attr_value[pos]; }
uint8_t operator[](int pos) const {
NIMBLE_CPP_DEBUG_ASSERT(pos < m_attr_len);
return m_attr_value[pos];
}
/** @brief Operator; Get the value as a std::vector<uint8_t>. */
operator std::vector<uint8_t>() const {
return std::vector<uint8_t>(m_attr_value, m_attr_value + m_attr_len); }
operator std::vector<uint8_t>() const { return std::vector<uint8_t>(m_attr_value, m_attr_value + m_attr_len); }
/** @brief Operator; Get the value as a std::string. */
operator std::string() const {
return std::string((char*)m_attr_value, m_attr_len); }
operator std::string() const { return std::string(reinterpret_cast<char*>(m_attr_value), m_attr_len); }
/** @brief Operator; Get the value as a const uint8_t*. */
operator const uint8_t*() const { return m_attr_value; }
/** @brief Operator; Append another NimBLEAttValue. */
NimBLEAttValue& operator +=(const NimBLEAttValue & source) {
return append(source.data(), source.size()); }
NimBLEAttValue& operator+=(const NimBLEAttValue& source) { return append(source.data(), source.size()); }
/** @brief Operator; Set the value from a std::string source. */
NimBLEAttValue& operator =(const std::string & source) {
setValue((uint8_t*)source.data(), (uint16_t)source.size()); return *this; }
NimBLEAttValue& operator=(const std::string& source) {
setValue(reinterpret_cast<const uint8_t*>(&source[0]), source.size());
return *this;
}
/** @brief Move assignment operator */
NimBLEAttValue& operator =(NimBLEAttValue && source);
NimBLEAttValue& operator=(NimBLEAttValue&& source);
/** @brief Copy assignment operator */
NimBLEAttValue& operator =(const NimBLEAttValue & source);
NimBLEAttValue& operator=(const NimBLEAttValue& source);
/** @brief Equality operator */
bool operator ==(const NimBLEAttValue & source) {
return (m_attr_len == source.size()) ?
memcmp(m_attr_value, source.data(), m_attr_len) == 0 : false; }
bool operator==(const NimBLEAttValue& source) const {
return (m_attr_len == source.size()) ? memcmp(m_attr_value, source.data(), m_attr_len) == 0 : false;
}
/** @brief Inequality operator */
bool operator !=(const NimBLEAttValue & source){ return !(*this == source); }
bool operator!=(const NimBLEAttValue& source) const { return !(*this == source); }
#ifdef NIMBLE_CPP_ARDUINO_STRING_AVAILABLE
# ifdef NIMBLE_CPP_ARDUINO_STRING_AVAILABLE
/** @brief Operator; Get the value as an Arduino String value. */
operator String() const { return String((char*)m_attr_value); }
#endif
operator String() const { return String(reinterpret_cast<char*>(m_attr_value)); }
# endif
};
inline NimBLEAttValue::NimBLEAttValue(uint16_t init_len, uint16_t max_len) {
m_attr_value = (uint8_t*)calloc(init_len + 1, 1);
assert(m_attr_value && "No Mem");
m_attr_max_len = std::min(BLE_ATT_ATTR_MAX_LEN, (int)max_len);
m_attr_len = 0;
m_capacity = init_len;
setTimeStamp(0);
}
inline NimBLEAttValue::NimBLEAttValue(const uint8_t *value, uint16_t len, uint16_t max_len)
: NimBLEAttValue(len, max_len) {
memcpy(m_attr_value, value, len);
m_attr_value[len] = '\0';
m_attr_len = len;
}
inline NimBLEAttValue::~NimBLEAttValue() {
if(m_attr_value != nullptr) {
free(m_attr_value);
}
}
inline NimBLEAttValue& NimBLEAttValue::operator =(NimBLEAttValue && source) {
if (this != &source){
free(m_attr_value);
m_attr_value = source.m_attr_value;
m_attr_max_len = source.m_attr_max_len;
m_attr_len = source.m_attr_len;
m_capacity = source.m_capacity;
setTimeStamp(source.getTimeStamp());
source.m_attr_value = nullptr;
}
return *this;
}
inline NimBLEAttValue& NimBLEAttValue::operator =(const NimBLEAttValue & source) {
if (this != &source) {
deepCopy(source);
}
return *this;
}
inline void NimBLEAttValue::deepCopy(const NimBLEAttValue & source) {
uint8_t* res = (uint8_t*)realloc( m_attr_value, source.m_capacity + 1);
assert(res && "deepCopy: realloc failed");
ble_npl_hw_enter_critical();
m_attr_value = res;
m_attr_max_len = source.m_attr_max_len;
m_attr_len = source.m_attr_len;
m_capacity = source.m_capacity;
setTimeStamp(source.getTimeStamp());
memcpy(m_attr_value, source.m_attr_value, m_attr_len + 1);
ble_npl_hw_exit_critical(0);
}
inline const uint8_t* NimBLEAttValue::getValue(time_t *timestamp) {
if(timestamp != nullptr) {
#if CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED
*timestamp = m_timestamp;
#else
*timestamp = 0;
#endif
}
return m_attr_value;
}
inline bool NimBLEAttValue::setValue(const uint8_t *value, uint16_t len) {
if (len > m_attr_max_len) {
NIMBLE_LOGE("NimBLEAttValue", "value exceeds max, len=%u, max=%u",
len, m_attr_max_len);
return false;
}
uint8_t *res = m_attr_value;
if (len > m_capacity) {
res = (uint8_t*)realloc(m_attr_value, (len + 1));
m_capacity = len;
}
assert(res && "setValue: realloc failed");
#if CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED
time_t t = time(nullptr);
#else
time_t t = 0;
#endif
ble_npl_hw_enter_critical();
m_attr_value = res;
memcpy(m_attr_value, value, len);
m_attr_value[len] = '\0';
m_attr_len = len;
setTimeStamp(t);
ble_npl_hw_exit_critical(0);
return true;
}
inline NimBLEAttValue& NimBLEAttValue::append(const uint8_t *value, uint16_t len) {
if (len < 1) {
return *this;
}
if ((m_attr_len + len) > m_attr_max_len) {
NIMBLE_LOGE("NimBLEAttValue", "val > max, len=%u, max=%u",
len, m_attr_max_len);
return *this;
}
uint8_t* res = m_attr_value;
uint16_t new_len = m_attr_len + len;
if (new_len > m_capacity) {
res = (uint8_t*)realloc(m_attr_value, (new_len + 1));
m_capacity = new_len;
}
assert(res && "append: realloc failed");
#if CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED
time_t t = time(nullptr);
#else
time_t t = 0;
#endif
ble_npl_hw_enter_critical();
m_attr_value = res;
memcpy(m_attr_value + m_attr_len, value, len);
m_attr_len = new_len;
m_attr_value[m_attr_len] = '\0';
setTimeStamp(t);
ble_npl_hw_exit_critical(0);
return *this;
}
#endif /*(CONFIG_BT_ENABLED) */
#endif /* MAIN_NIMBLEATTVALUE_H_ */
#endif /* NIMBLE_CPP_ATTVALUE_H_ */

View File

@@ -0,0 +1,50 @@
/*
* NimBLEAttribute.h
*
* Created: on July 28 2024
* Author H2zero
*/
#ifndef NIMBLE_CPP_ATTRIBUTE_H_
#define NIMBLE_CPP_ATTRIBUTE_H_
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && (defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) || defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL))
# include "NimBLEUUID.h"
/**
* @brief A base class for BLE attributes.
*/
class NimBLEAttribute {
public:
/**
* @brief Get the UUID of the attribute.
* @return The UUID.
*/
const NimBLEUUID& getUUID() const { return m_uuid; }
/**
* @brief Get the handle of the attribute.
*/
uint16_t getHandle() const { return m_handle; };
protected:
/**
* @brief Construct a new NimBLEAttribute object.
* @param [in] handle The handle of the attribute.
* @param [in] uuid The UUID of the attribute.
*/
NimBLEAttribute(const NimBLEUUID& uuid, uint16_t handle) : m_uuid{uuid}, m_handle{handle} {}
/**
* @brief Destroy the NimBLEAttribute object.
*/
~NimBLEAttribute() = default;
const NimBLEUUID m_uuid{};
uint16_t m_handle{0};
};
#endif // CONFIG_BT_ENABLED && (CONFIG_BT_NIMBLE_ROLE_PERIPHERAL || CONFIG_BT_NIMBLE_ROLE_CENTRAL)
#endif // NIMBLE_CPP_ATTRIBUTE_H_

View File

@@ -79,7 +79,7 @@ uint16_t NimBLEBeacon::getMinor() {
* @return The UUID advertised.
*/
NimBLEUUID NimBLEBeacon::getProximityUUID() {
return NimBLEUUID(m_beaconData.proximityUUID, 16, true);
return NimBLEUUID(m_beaconData.proximityUUID, 16).reverseByteOrder();
}
@@ -140,9 +140,7 @@ void NimBLEBeacon::setMinor(uint16_t minor) {
void NimBLEBeacon::setProximityUUID(const NimBLEUUID &uuid) {
NimBLEUUID temp_uuid = uuid;
temp_uuid.to128();
std::reverse_copy(temp_uuid.getNative()->u128.value,
temp_uuid.getNative()->u128.value + 16,
m_beaconData.proximityUUID);
std::reverse_copy(temp_uuid.getValue(), temp_uuid.getValue() + 16, m_beaconData.proximityUUID);
} // setProximityUUID

View File

@@ -13,18 +13,16 @@
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#include "NimBLECharacteristic.h"
#include "NimBLE2904.h"
#include "NimBLEDevice.h"
#include "NimBLELog.h"
# include "NimBLECharacteristic.h"
# include "NimBLE2904.h"
# include "NimBLEDevice.h"
# include "NimBLELog.h"
#define NULL_HANDLE (0xffff)
#define NIMBLE_SUB_NOTIFY 0x0001
#define NIMBLE_SUB_INDICATE 0x0002
# define NIMBLE_SUB_NOTIFY 0x0001
# define NIMBLE_SUB_INDICATE 0x0002
static NimBLECharacteristicCallbacks defaultCallback;
static const char* LOG_TAG = "NimBLECharacteristic";
static const char* LOG_TAG = "NimBLECharacteristic";
/**
* @brief Construct a characteristic
@@ -33,10 +31,8 @@ static const char* LOG_TAG = "NimBLECharacteristic";
* @param [in] max_len - The maximum length in bytes that the characteristic value can hold. (Default: 512 bytes for esp32, 20 for all others).
* @param [in] pService - pointer to the service instance this characteristic belongs to.
*/
NimBLECharacteristic::NimBLECharacteristic(const char* uuid, uint16_t properties,
uint16_t max_len, NimBLEService* pService)
: NimBLECharacteristic(NimBLEUUID(uuid), properties, max_len, pService) {
}
NimBLECharacteristic::NimBLECharacteristic(const char* uuid, uint16_t properties, uint16_t max_len, NimBLEService* pService)
: NimBLECharacteristic(NimBLEUUID(uuid), properties, max_len, pService) {}
/**
* @brief Construct a characteristic
@@ -45,27 +41,20 @@ NimBLECharacteristic::NimBLECharacteristic(const char* uuid, uint16_t properties
* @param [in] max_len - The maximum length in bytes that the characteristic value can hold. (Default: 512 bytes for esp32, 20 for all others).
* @param [in] pService - pointer to the service instance this characteristic belongs to.
*/
NimBLECharacteristic::NimBLECharacteristic(const NimBLEUUID &uuid, uint16_t properties,
uint16_t max_len, NimBLEService* pService)
: m_value(std::min(CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH , (int)max_len), max_len) {
m_uuid = uuid;
m_handle = NULL_HANDLE;
m_properties = properties;
m_pCallbacks = &defaultCallback;
m_pService = pService;
m_removed = 0;
NimBLECharacteristic::NimBLECharacteristic(const NimBLEUUID& uuid, uint16_t properties, uint16_t max_len, NimBLEService* pService)
: NimBLELocalValueAttribute{uuid, 0, max_len}, m_pCallbacks{&defaultCallback}, m_pService{pService} {
setProperties(properties);
} // NimBLECharacteristic
/**
* @brief Destructor.
*/
NimBLECharacteristic::~NimBLECharacteristic() {
for(auto &it : m_dscVec) {
delete it;
for (const auto& dsc : m_dscVec) {
delete dsc;
}
} // ~NimBLECharacteristic
/**
* @brief Create a new BLE Descriptor associated with this characteristic.
* @param [in] uuid - The UUID of the descriptor.
@@ -77,7 +66,6 @@ NimBLEDescriptor* NimBLECharacteristic::createDescriptor(const char* uuid, uint3
return createDescriptor(NimBLEUUID(uuid), properties, max_len);
}
/**
* @brief Create a new BLE Descriptor associated with this characteristic.
* @param [in] uuid - The UUID of the descriptor.
@@ -85,11 +73,9 @@ NimBLEDescriptor* NimBLECharacteristic::createDescriptor(const char* uuid, uint3
* @param [in] max_len - The max length in bytes of the descriptor value.
* @return The new BLE descriptor.
*/
NimBLEDescriptor* NimBLECharacteristic::createDescriptor(const NimBLEUUID &uuid, uint32_t properties, uint16_t max_len) {
NimBLEDescriptor* NimBLECharacteristic::createDescriptor(const NimBLEUUID& uuid, uint32_t properties, uint16_t max_len) {
NimBLEDescriptor* pDescriptor = nullptr;
if(uuid == NimBLEUUID(uint16_t(0x2902))) {
assert(0 && "0x2902 descriptors cannot be manually created");
} else if (uuid == NimBLEUUID(uint16_t(0x2904))) {
if (uuid == NimBLEUUID(uint16_t(0x2904))) {
pDescriptor = new NimBLE2904(this);
} else {
pDescriptor = new NimBLEDescriptor(uuid, properties, max_len, this);
@@ -99,24 +85,22 @@ NimBLEDescriptor* NimBLECharacteristic::createDescriptor(const NimBLEUUID &uuid,
return pDescriptor;
} // createDescriptor
/**
* @brief Add a descriptor to the characteristic.
* @param [in] pDescriptor A pointer to the descriptor to add.
*/
void NimBLECharacteristic::addDescriptor(NimBLEDescriptor *pDescriptor) {
void NimBLECharacteristic::addDescriptor(NimBLEDescriptor* pDescriptor) {
bool foundRemoved = false;
if(pDescriptor->m_removed > 0) {
for(auto& it : m_dscVec) {
if(it == pDescriptor) {
if (pDescriptor->getRemoved() > 0) {
for (const auto& dsc : m_dscVec) {
if (dsc == pDescriptor) {
foundRemoved = true;
pDescriptor->m_removed = 0;
pDescriptor->setRemoved(0);
}
}
}
if(!foundRemoved) {
if (!foundRemoved) {
m_dscVec.push_back(pDescriptor);
}
@@ -124,21 +108,20 @@ void NimBLECharacteristic::addDescriptor(NimBLEDescriptor *pDescriptor) {
NimBLEDevice::getServer()->serviceChanged();
}
/**
* @brief Remove a descriptor from the characteristic.
* @param[in] pDescriptor A pointer to the descriptor instance to remove from the characteristic.
* @param[in] deleteDsc If true it will delete the descriptor instance and free it's resources.
*/
void NimBLECharacteristic::removeDescriptor(NimBLEDescriptor *pDescriptor, bool deleteDsc) {
void NimBLECharacteristic::removeDescriptor(NimBLEDescriptor* pDescriptor, bool deleteDsc) {
// Check if the descriptor was already removed and if so, check if this
// is being called to delete the object and do so if requested.
// Otherwise, ignore the call and return.
if(pDescriptor->m_removed > 0) {
if(deleteDsc) {
for(auto it = m_dscVec.begin(); it != m_dscVec.end(); ++it) {
if (pDescriptor->getRemoved() > 0) {
if (deleteDsc) {
for (auto it = m_dscVec.begin(); it != m_dscVec.end(); ++it) {
if ((*it) == pDescriptor) {
delete *it;
delete (*it);
m_dscVec.erase(it);
break;
}
@@ -148,30 +131,28 @@ void NimBLECharacteristic::removeDescriptor(NimBLEDescriptor *pDescriptor, bool
return;
}
pDescriptor->m_removed = deleteDsc ? NIMBLE_ATT_REMOVE_DELETE : NIMBLE_ATT_REMOVE_HIDE;
pDescriptor->setRemoved(deleteDsc ? NIMBLE_ATT_REMOVE_DELETE : NIMBLE_ATT_REMOVE_HIDE);
NimBLEDevice::getServer()->serviceChanged();
} // removeDescriptor
/**
* @brief Return the BLE Descriptor for the given UUID.
* @param [in] uuid The UUID of the descriptor.
* @return A pointer to the descriptor object or nullptr if not found.
*/
NimBLEDescriptor* NimBLECharacteristic::getDescriptorByUUID(const char* uuid) {
NimBLEDescriptor* NimBLECharacteristic::getDescriptorByUUID(const char* uuid) const {
return getDescriptorByUUID(NimBLEUUID(uuid));
} // getDescriptorByUUID
/**
* @brief Return the BLE Descriptor for the given UUID.
* @param [in] uuid The UUID of the descriptor.
* @return A pointer to the descriptor object or nullptr if not found.
*/
NimBLEDescriptor* NimBLECharacteristic::getDescriptorByUUID(const NimBLEUUID &uuid) {
for (auto &it : m_dscVec) {
if (it->getUUID() == uuid) {
return it;
NimBLEDescriptor* NimBLECharacteristic::getDescriptorByUUID(const NimBLEUUID& uuid) const {
for (const auto& dsc : m_dscVec) {
if (dsc->getUUID() == uuid) {
return dsc;
}
}
return nullptr;
@@ -182,350 +163,230 @@ NimBLEDescriptor* NimBLECharacteristic::getDescriptorByUUID(const NimBLEUUID &uu
* @param [in] handle The handle of the descriptor.
* @return A pointer to the descriptor object or nullptr if not found.
*/
NimBLEDescriptor *NimBLECharacteristic::getDescriptorByHandle(uint16_t handle) {
for (auto &it : m_dscVec) {
if (it->getHandle() == handle) {
return it;
NimBLEDescriptor* NimBLECharacteristic::getDescriptorByHandle(uint16_t handle) const {
for (const auto& dsc : m_dscVec) {
if (dsc->getHandle() == handle) {
return dsc;
}
}
return nullptr;
}
/**
* @brief Get the handle of the characteristic.
* @return The handle of the characteristic.
*/
uint16_t NimBLECharacteristic::getHandle() {
return m_handle;
} // getHandle
} // getDescriptorByHandle
/**
* @brief Get the properties of the characteristic.
* @return The properties of the characteristic.
*/
uint16_t NimBLECharacteristic::getProperties() {
uint16_t NimBLECharacteristic::getProperties() const {
return m_properties;
} // getProperties
/**
* @brief Get the service associated with this characteristic.
* @brief Get the service that owns this characteristic.
*/
NimBLEService* NimBLECharacteristic::getService() {
NimBLEService* NimBLECharacteristic::getService() const {
return m_pService;
} // getService
void NimBLECharacteristic::setService(NimBLEService *pService) {
void NimBLECharacteristic::setService(NimBLEService* pService) {
m_pService = pService;
}
/**
* @brief Get the UUID of the characteristic.
* @return The UUID of the characteristic.
*/
NimBLEUUID NimBLECharacteristic::getUUID() {
return m_uuid;
} // getUUID
/**
* @brief Retrieve the current value of the characteristic.
* @return The NimBLEAttValue containing the current characteristic value.
*/
NimBLEAttValue NimBLECharacteristic::getValue(time_t *timestamp) {
if(timestamp != nullptr) {
m_value.getValue(timestamp);
}
return m_value;
} // getValue
/**
* @brief Retrieve the the current data length of the characteristic.
* @return The length of the current characteristic data.
*/
size_t NimBLECharacteristic::getDataLength() {
return m_value.size();
}
/**
* @brief STATIC callback to handle events from the NimBLE stack.
*/
int NimBLECharacteristic::handleGapEvent(uint16_t conn_handle, uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt,
void *arg)
{
if (conn_handle > BLE_HCI_LE_CONN_HANDLE_MAX)
{
NIMBLE_LOGW(LOG_TAG, "Conn_handle (%d) is above the maximum value (%d)", conn_handle, BLE_HCI_LE_CONN_HANDLE_MAX);
return BLE_ATT_ERR_INVALID_HANDLE;
}
const ble_uuid_t *uuid;
int rc;
NimBLEConnInfo peerInfo;
NimBLECharacteristic* pCharacteristic = (NimBLECharacteristic*)arg;
NIMBLE_LOGD(LOG_TAG, "Characteristic %s %s event", pCharacteristic->getUUID().toString().c_str(),
ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR ? "Read" : "Write");
uuid = ctxt->chr->uuid;
if(ble_uuid_cmp(uuid, &pCharacteristic->getUUID().getNative()->u) == 0){
switch(ctxt->op) {
case BLE_GATT_ACCESS_OP_READ_CHR: {
ble_gap_conn_find(conn_handle, &peerInfo.m_desc);
// If the packet header is only 8 bytes this is a follow up of a long read
// so we don't want to call the onRead() callback again.
if(ctxt->om->om_pkthdr_len > 8 ||
pCharacteristic->m_value.size() <= (ble_att_mtu(peerInfo.m_desc.conn_handle) - 3)) {
pCharacteristic->m_pCallbacks->onRead(pCharacteristic, peerInfo);
}
ble_npl_hw_enter_critical();
rc = os_mbuf_append(ctxt->om, pCharacteristic->m_value.data(), pCharacteristic->m_value.size());
ble_npl_hw_exit_critical(0);
return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
}
case BLE_GATT_ACCESS_OP_WRITE_CHR: {
uint16_t att_max_len = pCharacteristic->m_value.max_size();
if (ctxt->om->om_len > att_max_len) {
return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
}
uint8_t buf[att_max_len];
size_t len = ctxt->om->om_len;
memcpy(buf, ctxt->om->om_data,len);
os_mbuf *next;
next = SLIST_NEXT(ctxt->om, om_next);
while(next != NULL){
if((len + next->om_len) > att_max_len) {
return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
}
memcpy(&buf[len], next->om_data, next->om_len);
len += next->om_len;
next = SLIST_NEXT(next, om_next);
}
ble_gap_conn_find(conn_handle, &peerInfo.m_desc);
pCharacteristic->setValue(buf, len);
pCharacteristic->m_pCallbacks->onWrite(pCharacteristic, peerInfo);
return 0;
}
default:
break;
}
}
return BLE_ATT_ERR_UNLIKELY;
}
} // setService
/**
* @brief Get the number of clients subscribed to the characteristic.
* @returns Number of clients subscribed to notifications / indications.
*/
size_t NimBLECharacteristic::getSubscribedCount() {
size_t NimBLECharacteristic::getSubscribedCount() const {
return m_subscribedVec.size();
}
/**
* @brief Set the subscribe status for this characteristic.\n
* This will maintain a vector of subscribed clients and their indicate/notify status.
*/
void NimBLECharacteristic::setSubscribe(struct ble_gap_event *event) {
NimBLEConnInfo peerInfo;
if(ble_gap_conn_find(event->subscribe.conn_handle, &peerInfo.m_desc) != 0) {
return;
}
void NimBLECharacteristic::setSubscribe(const ble_gap_event* event, const NimBLEConnInfo& connInfo) {
uint16_t subVal = 0;
if(event->subscribe.cur_notify > 0 && (m_properties & NIMBLE_PROPERTY::NOTIFY)) {
if (event->subscribe.cur_notify > 0 && (m_properties & NIMBLE_PROPERTY::NOTIFY)) {
subVal |= NIMBLE_SUB_NOTIFY;
}
if(event->subscribe.cur_indicate && (m_properties & NIMBLE_PROPERTY::INDICATE)) {
if (event->subscribe.cur_indicate && (m_properties & NIMBLE_PROPERTY::INDICATE)) {
subVal |= NIMBLE_SUB_INDICATE;
}
NIMBLE_LOGI(LOG_TAG, "New subscribe value for conn: %d val: %d",
event->subscribe.conn_handle, subVal);
NIMBLE_LOGI(LOG_TAG, "New subscribe value for conn: %d val: %d", connInfo.getConnHandle(), subVal);
if(!event->subscribe.cur_indicate && event->subscribe.prev_indicate) {
NimBLEDevice::getServer()->clearIndicateWait(event->subscribe.conn_handle);
if (!event->subscribe.cur_indicate && event->subscribe.prev_indicate) {
NimBLEDevice::getServer()->clearIndicateWait(connInfo.getConnHandle());
}
auto it = m_subscribedVec.begin();
for(;it != m_subscribedVec.end(); ++it) {
if((*it).first == event->subscribe.conn_handle) {
for (; it != m_subscribedVec.end(); ++it) {
if ((*it).first == connInfo.getConnHandle()) {
break;
}
}
if(subVal > 0) {
if(it == m_subscribedVec.end()) {
m_subscribedVec.push_back({event->subscribe.conn_handle, subVal});
if (subVal > 0) {
if (it == m_subscribedVec.end()) {
m_subscribedVec.push_back({connInfo.getConnHandle(), subVal});
} else {
(*it).second = subVal;
}
} else if(it != m_subscribedVec.end()) {
} else if (it != m_subscribedVec.end()) {
m_subscribedVec.erase(it);
}
m_pCallbacks->onSubscribe(this, peerInfo, subVal);
m_pCallbacks->onSubscribe(this, connInfo, subVal);
}
/**
* @brief Send an indication.
* @param[in] conn_handle Connection handle to send an individual indication, or BLE_HS_CONN_HANDLE_NONE to send
* the indication to all subscribed clients.
*/
void NimBLECharacteristic::indicate() {
notify(false);
void NimBLECharacteristic::indicate(uint16_t conn_handle) const {
sendValue(m_value.data(), m_value.size(), false, conn_handle);
} // indicate
/**
* @brief Send an indication.
* @param[in] value A pointer to the data to send.
* @param[in] length The length of the data to send.
* @param[in] conn_handle Connection handle to send an individual indication, or BLE_HS_CONN_HANDLE_NONE to send
* the indication to all subscribed clients.
*/
void NimBLECharacteristic::indicate(const uint8_t* value, size_t length) {
notify(value, length, false);
void NimBLECharacteristic::indicate(const uint8_t* value, size_t length, uint16_t conn_handle) const {
sendValue(value, length, false, conn_handle);
} // indicate
/**
* @brief Send an indication.
* @param[in] value A std::vector<uint8_t> containing the value to send as the notification value.
* @param[in] conn_handle Connection handle to send an individual indication, or BLE_HS_CONN_HANDLE_NONE to send
* the indication to all subscribed clients.
*/
void NimBLECharacteristic::indicate(const std::vector<uint8_t>& value) {
notify(value.data(), value.size(), false);
void NimBLECharacteristic::indicate(const std::vector<uint8_t>& value, uint16_t conn_handle) const {
sendValue(value.data(), value.size(), false, conn_handle);
} // indicate
/**
* @brief Send a notification or indication.
* @param[in] is_notification if true sends a notification, false sends an indication.
* @param[in] conn_handle Connection handle to send individual notification, or BLE_HCI_LE_CONN_HANDLE_MAX + 1 to send notification to all subscribed clients.
* @brief Send a notification.
* @param[in] conn_handle Connection handle to send an individual notification, or BLE_HS_CONN_HANDLE_NONE to send
* the notification to all subscribed clients.
*/
void NimBLECharacteristic::notify(bool is_notification, uint16_t conn_handle) {
notify(m_value.data(), m_value.length(), is_notification, conn_handle);
void NimBLECharacteristic::notify(uint16_t conn_handle) const {
sendValue(m_value.data(), m_value.size(), true, conn_handle);
} // notify
/**
* @brief Send a notification.
* @param[in] value A pointer to the data to send.
* @param[in] length The length of the data to send.
* @param[in] conn_handle Connection handle to send an individual notification, or BLE_HS_CONN_HANDLE_NONE to send
* the notification to all subscribed clients.
*/
void NimBLECharacteristic::notify(const uint8_t* value, size_t length, uint16_t conn_handle) const {
sendValue(value, length, true, conn_handle);
} // indicate
/**
* @brief Send a notification or indication.
* @brief Send a notification.
* @param[in] value A std::vector<uint8_t> containing the value to send as the notification value.
* @param[in] is_notification if true sends a notification, false sends an indication.
* @param[in] conn_handle Connection handle to send individual notification, or BLE_HCI_LE_CONN_HANDLE_MAX + 1 to send notification to all subscribed clients.
* @param[in] conn_handle Connection handle to send an individual notification, or BLE_HS_CONN_HANDLE_NONE to send
* the notification to all subscribed clients.
*/
void NimBLECharacteristic::notify(const std::vector<uint8_t>& value, bool is_notification, uint16_t conn_handle) {
notify(value.data(), value.size(), is_notification, conn_handle);
void NimBLECharacteristic::notify(const std::vector<uint8_t>& value, uint16_t conn_handle) const {
sendValue(value.data(), value.size(), true, conn_handle);
} // notify
/**
* @brief Send a notification or indication.
* @brief Sends a notification or indication.
* @param[in] value A pointer to the data to send.
* @param[in] length The length of the data to send.
* @param[in] is_notification if true sends a notification, false sends an indication.
* @param[in] conn_handle Connection handle to send individual notification, or BLE_HCI_LE_CONN_HANDLE_MAX + 1 to send notification to all subscribed clients.
* @param[in] conn_handle Connection handle to send to a specific peer, or BLE_HS_CONN_HANDLE_NONE to send
* to all subscribed clients.
*/
void NimBLECharacteristic::notify(const uint8_t* value, size_t length, bool is_notification, uint16_t conn_handle) {
NIMBLE_LOGD(LOG_TAG, ">> notify: length: %d", length);
void NimBLECharacteristic::sendValue(const uint8_t* value, size_t length, bool is_notification, uint16_t conn_handle) const {
NIMBLE_LOGD(LOG_TAG, ">> sendValue");
if(!(m_properties & NIMBLE_PROPERTY::NOTIFY) &&
!(m_properties & NIMBLE_PROPERTY::INDICATE))
{
NIMBLE_LOGE(LOG_TAG,
"<< notify-Error; Notify/indicate not enabled for characteristic: %s",
std::string(getUUID()).c_str());
}
if (m_subscribedVec.size() == 0) {
NIMBLE_LOGD(LOG_TAG, "<< notify: No clients subscribed.");
if (is_notification && !(getProperties() & NIMBLE_PROPERTY::NOTIFY)) {
NIMBLE_LOGE(LOG_TAG, "<< sendValue: notification not enabled for characteristic");
return;
}
m_pCallbacks->onNotify(this);
if (!is_notification && !(getProperties() & NIMBLE_PROPERTY::INDICATE)) {
NIMBLE_LOGE(LOG_TAG, "<< sendValue: indication not enabled for characteristic");
return;
}
bool reqSec = (m_properties & BLE_GATT_CHR_F_READ_AUTHEN) ||
(m_properties & BLE_GATT_CHR_F_READ_AUTHOR) ||
(m_properties & BLE_GATT_CHR_F_READ_ENC);
int rc = 0;
if (!m_subscribedVec.size()) {
NIMBLE_LOGD(LOG_TAG, "<< sendValue: No clients subscribed.");
return;
}
for (auto &it : m_subscribedVec) {
// check if need a specific client
for (const auto& it : m_subscribedVec) {
// check if connected and subscribed
if (!it.second) {
continue;
}
// sending to a specific client?
if ((conn_handle <= BLE_HCI_LE_CONN_HANDLE_MAX) && (it.first != conn_handle)) {
continue;
}
uint16_t _mtu = getService()->getServer()->getPeerMTU(it.first) - 3;
if (is_notification && !(it.second & NIMBLE_SUB_NOTIFY)) {
continue;
}
// check if connected and subscribed
if(_mtu == 0 || it.second == 0) {
if (!is_notification && !(it.second & NIMBLE_SUB_INDICATE)) {
continue;
}
// check if security requirements are satisfied
if(reqSec) {
struct ble_gap_conn_desc desc;
rc = ble_gap_conn_find(it.first, &desc);
if(rc != 0 || !desc.sec_state.encrypted) {
if ((getProperties() & BLE_GATT_CHR_F_READ_AUTHEN) || (getProperties() & BLE_GATT_CHR_F_READ_AUTHOR) ||
(getProperties() & BLE_GATT_CHR_F_READ_ENC)) {
ble_gap_conn_desc desc;
if (ble_gap_conn_find(it.first, &desc) != 0 || !desc.sec_state.encrypted) {
continue;
}
}
if (length > _mtu) {
NIMBLE_LOGW(LOG_TAG, "- Truncating to %d bytes (maximum notify size)", _mtu);
}
if(is_notification && (!(it.second & NIMBLE_SUB_NOTIFY))) {
NIMBLE_LOGW(LOG_TAG,
"Sending notification to client subscribed to indications, sending indication instead");
is_notification = false;
}
if(!is_notification && (!(it.second & NIMBLE_SUB_INDICATE))) {
NIMBLE_LOGW(LOG_TAG,
"Sending indication to client subscribed to notification, sending notification instead");
is_notification = true;
}
// don't create the m_buf until we are sure to send the data or else
// we could be allocating a buffer that doesn't get released.
// We also must create it in each loop iteration because it is consumed with each host call.
os_mbuf *om = ble_hs_mbuf_from_flat(value, length);
os_mbuf* om = ble_hs_mbuf_from_flat(value, length);
if (!om) {
NIMBLE_LOGE(LOG_TAG, "<< sendValue: failed to allocate mbuf");
return;
}
if(!is_notification && (m_properties & NIMBLE_PROPERTY::INDICATE)) {
if(!NimBLEDevice::getServer()->setIndicateWait(it.first)) {
NIMBLE_LOGE(LOG_TAG, "prior Indication in progress");
os_mbuf_free_chain(om);
return;
if (is_notification) {
ble_gattc_notify_custom(it.first, getHandle(), om);
} else {
if (!NimBLEDevice::getServer()->setIndicateWait(it.first)) {
NIMBLE_LOGE(LOG_TAG, "<< sendValue: waiting for previous indicate");
os_mbuf_free_chain(om);
return;
}
rc = ble_gattc_indicate_custom(it.first, m_handle, om);
if(rc != 0){
if (ble_gattc_indicate_custom(it.first, getHandle(), om) != 0) {
NimBLEDevice::getServer()->clearIndicateWait(it.first);
}
} else {
ble_gattc_notify_custom(it.first, m_handle, om);
}
}
NIMBLE_LOGD(LOG_TAG, "<< notify");
} // Notify
NIMBLE_LOGD(LOG_TAG, "<< sendValue");
} // sendValue
void NimBLECharacteristic::readEvent(const NimBLEConnInfo& connInfo) {
m_pCallbacks->onRead(this, connInfo);
}
void NimBLECharacteristic::writeEvent(const uint8_t* val, uint16_t len, const NimBLEConnInfo& connInfo) {
setValue(val, len);
m_pCallbacks->onWrite(this, connInfo);
}
/**
* @brief Set the callback handlers for this characteristic.
@@ -533,60 +394,31 @@ void NimBLECharacteristic::notify(const uint8_t* value, size_t length, bool is_n
* used to define any callbacks for the characteristic.
*/
void NimBLECharacteristic::setCallbacks(NimBLECharacteristicCallbacks* pCallbacks) {
if (pCallbacks != nullptr){
if (pCallbacks != nullptr) {
m_pCallbacks = pCallbacks;
} else {
m_pCallbacks = &defaultCallback;
}
} // setCallbacks
/**
* @brief Get the callback handlers for this characteristic.
*/
NimBLECharacteristicCallbacks* NimBLECharacteristic::getCallbacks() {
NimBLECharacteristicCallbacks* NimBLECharacteristic::getCallbacks() const {
return m_pCallbacks;
} //getCallbacks
/**
* @brief Set the value of the characteristic from a data buffer .
* @param [in] data The data buffer to set for the characteristic.
* @param [in] length The number of bytes in the data buffer.
*/
void NimBLECharacteristic::setValue(const uint8_t* data, size_t length) {
#if CONFIG_NIMBLE_CPP_LOG_LEVEL >= 4
char* pHex = NimBLEUtils::buildHexData(nullptr, data, length);
NIMBLE_LOGD(LOG_TAG, ">> setValue: length=%d, data=%s, characteristic UUID=%s",
length, pHex, getUUID().toString().c_str());
free(pHex);
#endif
m_value.setValue(data, length);
NIMBLE_LOGD(LOG_TAG, "<< setValue");
} // setValue
/**
* @brief Set the value of the characteristic from a `std::vector<uint8_t>`.\n
* @param [in] vec The std::vector<uint8_t> reference to set the characteristic value from.
*/
void NimBLECharacteristic::setValue(const std::vector<uint8_t>& vec) {
return setValue((uint8_t*)&vec[0], vec.size());
}// setValue
} // getCallbacks
/**
* @brief Return a string representation of the characteristic.
* @return A string representation of the characteristic.
*/
std::string NimBLECharacteristic::toString() {
std::string NimBLECharacteristic::toString() const {
std::string res = "UUID: " + m_uuid.toString() + ", handle : 0x";
char hex[5];
snprintf(hex, sizeof(hex), "%04x", m_handle);
char hex[5];
snprintf(hex, sizeof(hex), "%04x", getHandle());
res += hex;
res += " ";
if (m_properties & BLE_GATT_CHR_PROP_READ ) res += "Read ";
if (m_properties & BLE_GATT_CHR_PROP_READ) res += "Read ";
if (m_properties & BLE_GATT_CHR_PROP_WRITE) res += "Write ";
if (m_properties & BLE_GATT_CHR_PROP_WRITE_NO_RSP) res += "WriteNoResponse ";
if (m_properties & BLE_GATT_CHR_PROP_BROADCAST) res += "Broadcast ";
@@ -595,36 +427,24 @@ std::string NimBLECharacteristic::toString() {
return res;
} // toString
/**
* @brief Callback function to support a read request.
* @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.
*/
void NimBLECharacteristicCallbacks::onRead(NimBLECharacteristic* pCharacteristic, NimBLEConnInfo& connInfo) {
void NimBLECharacteristicCallbacks::onRead(NimBLECharacteristic* pCharacteristic, const NimBLEConnInfo& connInfo) {
NIMBLE_LOGD("NimBLECharacteristicCallbacks", "onRead: default");
} // onRead
/**
* @brief Callback function to support a write request.
* @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.
*/
void NimBLECharacteristicCallbacks::onWrite(NimBLECharacteristic* pCharacteristic, NimBLEConnInfo& connInfo) {
void NimBLECharacteristicCallbacks::onWrite(NimBLECharacteristic* pCharacteristic, const NimBLEConnInfo& connInfo) {
NIMBLE_LOGD("NimBLECharacteristicCallbacks", "onWrite: default");
} // onWrite
/**
* @brief Callback function to support a Notify request.
* @param [in] pCharacteristic The characteristic that is the source of the event.
*/
void NimBLECharacteristicCallbacks::onNotify(NimBLECharacteristic* pCharacteristic) {
NIMBLE_LOGD("NimBLECharacteristicCallbacks", "onNotify: default");
} // onNotify
/**
* @brief Callback function to support a Notify/Indicate Status report.
* @param [in] pCharacteristic The characteristic that is the source of the event.
@@ -636,7 +456,6 @@ void NimBLECharacteristicCallbacks::onStatus(NimBLECharacteristic* pCharacterist
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.
@@ -648,9 +467,8 @@ void NimBLECharacteristicCallbacks::onStatus(NimBLECharacteristic* pCharacterist
* * 3 = Notifications and Indications
*/
void NimBLECharacteristicCallbacks::onSubscribe(NimBLECharacteristic* pCharacteristic,
NimBLEConnInfo& connInfo,
uint16_t subValue)
{
const NimBLEConnInfo& connInfo,
uint16_t subValue) {
NIMBLE_LOGD("NimBLECharacteristicCallbacks", "onSubscribe: default");
}

View File

@@ -11,131 +11,71 @@
* Author: kolban
*/
#ifndef MAIN_NIMBLECHARACTERISTIC_H_
#define MAIN_NIMBLECHARACTERISTIC_H_
#ifndef NIMBLE_CPP_CHARACTERISTIC_H_
#define NIMBLE_CPP_CHARACTERISTIC_H_
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#if defined(CONFIG_NIMBLE_CPP_IDF)
#include "host/ble_hs.h"
#else
#include "nimble/nimble/host/include/host/ble_hs.h"
#endif
/**** FIX COMPILATION ****/
#undef min
#undef max
/**************************/
typedef enum {
READ = BLE_GATT_CHR_F_READ,
READ_ENC = BLE_GATT_CHR_F_READ_ENC,
READ_AUTHEN = BLE_GATT_CHR_F_READ_AUTHEN,
READ_AUTHOR = BLE_GATT_CHR_F_READ_AUTHOR,
WRITE = BLE_GATT_CHR_F_WRITE,
WRITE_NR = BLE_GATT_CHR_F_WRITE_NO_RSP,
WRITE_ENC = BLE_GATT_CHR_F_WRITE_ENC,
WRITE_AUTHEN = BLE_GATT_CHR_F_WRITE_AUTHEN,
WRITE_AUTHOR = BLE_GATT_CHR_F_WRITE_AUTHOR,
BROADCAST = BLE_GATT_CHR_F_BROADCAST,
NOTIFY = BLE_GATT_CHR_F_NOTIFY,
INDICATE = BLE_GATT_CHR_F_INDICATE
} NIMBLE_PROPERTY;
#include "NimBLEService.h"
#include "NimBLEDescriptor.h"
#include "NimBLEAttValue.h"
#include "NimBLEConnInfo.h"
#include <string>
#include <vector>
class NimBLEService;
class NimBLEDescriptor;
class NimBLECharacteristicCallbacks;
class NimBLECharacteristic;
# include "NimBLELocalValueAttribute.h"
# include "NimBLEServer.h"
# include "NimBLEService.h"
# include "NimBLEDescriptor.h"
# include "NimBLEAttValue.h"
# include "NimBLEConnInfo.h"
# include <string>
# include <vector>
/**
* @brief The model of a %BLE Characteristic.
* @brief The model of a BLE Characteristic.
*
* A BLE Characteristic is an identified value container that manages a value. It is exposed by a BLE server and
* can be read and written to by a %BLE client.
* A BLE Characteristic is an identified value container that manages a value. It is exposed by a BLE service and
* can be read and written to by a BLE client.
*/
class NimBLECharacteristic {
public:
NimBLECharacteristic(const char* uuid,
uint16_t properties =
NIMBLE_PROPERTY::READ |
NIMBLE_PROPERTY::WRITE,
uint16_t max_len = BLE_ATT_ATTR_MAX_LEN,
NimBLEService* pService = nullptr);
NimBLECharacteristic(const NimBLEUUID &uuid,
uint16_t properties =
NIMBLE_PROPERTY::READ |
NIMBLE_PROPERTY::WRITE,
uint16_t max_len = BLE_ATT_ATTR_MAX_LEN,
NimBLEService* pService = nullptr);
class NimBLECharacteristic : public NimBLELocalValueAttribute {
public:
NimBLECharacteristic(const char* uuid,
uint16_t properties = NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE,
uint16_t max_len = BLE_ATT_ATTR_MAX_LEN,
NimBLEService* pService = nullptr);
NimBLECharacteristic(const NimBLEUUID& uuid,
uint16_t properties = NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE,
uint16_t max_len = BLE_ATT_ATTR_MAX_LEN,
NimBLEService* pService = nullptr);
~NimBLECharacteristic();
uint16_t getHandle();
NimBLEUUID getUUID();
std::string toString();
void indicate();
void indicate(const uint8_t* value, size_t length);
void indicate(const std::vector<uint8_t>& value);
void notify(bool is_notification = true, uint16_t conn_handle = BLE_HCI_LE_CONN_HANDLE_MAX + 1);
void notify(const uint8_t* value, size_t length, bool is_notification = true, uint16_t conn_handle = BLE_HCI_LE_CONN_HANDLE_MAX + 1);
void notify(const std::vector<uint8_t>& value, bool is_notification = true, uint16_t conn_handle = BLE_HCI_LE_CONN_HANDLE_MAX + 1);
size_t getSubscribedCount();
void addDescriptor(NimBLEDescriptor *pDescriptor);
NimBLEDescriptor* getDescriptorByUUID(const char* uuid);
NimBLEDescriptor* getDescriptorByUUID(const NimBLEUUID &uuid);
NimBLEDescriptor* getDescriptorByHandle(uint16_t handle);
void removeDescriptor(NimBLEDescriptor *pDescriptor, bool deleteDsc = false);
NimBLEService* getService();
uint16_t getProperties();
NimBLEAttValue getValue(time_t *timestamp = nullptr);
size_t getDataLength();
void setValue(const uint8_t* data, size_t size);
void setValue(const std::vector<uint8_t>& vec);
void setCallbacks(NimBLECharacteristicCallbacks* pCallbacks);
std::string toString() const;
size_t getSubscribedCount() const;
void addDescriptor(NimBLEDescriptor* pDescriptor);
void removeDescriptor(NimBLEDescriptor* pDescriptor, bool deleteDsc = false);
uint16_t getProperties() const;
void setCallbacks(NimBLECharacteristicCallbacks* pCallbacks);
void indicate(uint16_t conn_handle = BLE_HS_CONN_HANDLE_NONE) const;
void indicate(const uint8_t* value, size_t length, uint16_t conn_handle = BLE_HS_CONN_HANDLE_NONE) const;
void indicate(const std::vector<uint8_t>& value, uint16_t conn_handle = BLE_HS_CONN_HANDLE_NONE) const;
void notify(uint16_t conn_handle = BLE_HS_CONN_HANDLE_NONE) const;
void notify(const uint8_t* value, size_t length, uint16_t conn_handle = BLE_HS_CONN_HANDLE_NONE) const;
void notify(const std::vector<uint8_t>& value, uint16_t conn_handle = BLE_HS_CONN_HANDLE_NONE) const;
NimBLEDescriptor* createDescriptor(const char* uuid,
uint32_t properties =
NIMBLE_PROPERTY::READ |
NIMBLE_PROPERTY::WRITE,
uint16_t max_len = BLE_ATT_ATTR_MAX_LEN);;
NimBLEDescriptor* createDescriptor(const NimBLEUUID &uuid,
uint32_t properties =
NIMBLE_PROPERTY::READ |
NIMBLE_PROPERTY::WRITE,
uint16_t max_len = BLE_ATT_ATTR_MAX_LEN);
NimBLECharacteristicCallbacks* getCallbacks();
uint32_t properties = NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE,
uint16_t max_len = BLE_ATT_ATTR_MAX_LEN);
NimBLEDescriptor* createDescriptor(const NimBLEUUID& uuid,
uint32_t properties = NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE,
uint16_t max_len = BLE_ATT_ATTR_MAX_LEN);
NimBLEDescriptor* getDescriptorByUUID(const char* uuid) const;
NimBLEDescriptor* getDescriptorByUUID(const NimBLEUUID& uuid) const;
NimBLEDescriptor* getDescriptorByHandle(uint16_t handle) const;
NimBLEService* getService() const;
NimBLECharacteristicCallbacks* getCallbacks() const;
/*********************** Template Functions ************************/
/**
* @brief Template to set the characteristic value to <type\>val.
* @param [in] s The value to set.
*/
template<typename T>
void setValue(const T &s) { m_value.setValue<T>(s); }
/**
* @brief Template to convert the characteristic data to <type\>.
* @tparam T The type to convert the data to.
* @param [in] timestamp (Optional) A pointer to a time_t struct to store the time the value was read.
* @param [in] skipSizeCheck (Optional) If true it will skip checking if the data size is less than <tt>sizeof(<type\>)</tt>.
* @return The data converted to <type\> or NULL if skipSizeCheck is false and the data is less than <tt>sizeof(<type\>)</tt>.
* @details <b>Use:</b> <tt>getValue<type>(&timestamp, skipSizeCheck);</tt>
*/
template<typename T>
T getValue(time_t *timestamp = nullptr, bool skipSizeCheck = false) {
return m_value.getValue<T>(timestamp, skipSizeCheck);
}
/**
* @brief Template to send a notification from a class type that has a c_str() and length() method.
* @tparam T The a reference to a class containing the data to send.
@@ -143,14 +83,14 @@ public:
* @param[in] is_notification if true sends a notification, false sends an indication.
* @details Only used if the <type\> has a `c_str()` method.
*/
template<typename T>
#ifdef _DOXYGEN_
template <typename T>
# ifdef _DOXYGEN_
void
#else
# else
typename std::enable_if<Has_c_str_len<T>::value, void>::type
#endif
notify(const T& value, bool is_notification = true) {
notify((uint8_t*)value.c_str(), value.length(), is_notification);
# endif
notify(const T& value, bool is_notification = true) const {
notify(reinterpret_cast<const uint8_t*>(value.c_str()), value.length(), is_notification);
}
/**
@@ -159,39 +99,35 @@ public:
* @param[in] value The <type\>value to set.
* @details Only used if the <type\> has a `c_str()` method.
*/
template<typename T>
#ifdef _DOXYGEN_
template <typename T>
# ifdef _DOXYGEN_
void
#else
# else
typename std::enable_if<Has_c_str_len<T>::value, void>::type
#endif
indicate(const T& value) {
indicate((uint8_t*)value.c_str(), value.length());
# endif
indicate(const T& value) const {
indicate(reinterpret_cast<const uint8_t*>(value.c_str()), value.length());
}
private:
private:
friend class NimBLEServer;
friend class NimBLEService;
friend class NimBLEServer;
friend class NimBLEService;
void setService(NimBLEService* pService);
void setSubscribe(const ble_gap_event* event, const NimBLEConnInfo& connInfo);
void readEvent(const NimBLEConnInfo& connInfo) override;
void writeEvent(const uint8_t* val, uint16_t len, const NimBLEConnInfo& connInfo) override;
void sendValue(const uint8_t* value,
size_t length,
bool is_notification = true,
uint16_t conn_handle = BLE_HS_CONN_HANDLE_NONE) const;
void setService(NimBLEService *pService);
void setSubscribe(struct ble_gap_event *event);
static int handleGapEvent(uint16_t conn_handle, uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt, void *arg);
NimBLEUUID m_uuid;
uint16_t m_handle;
uint16_t m_properties;
NimBLECharacteristicCallbacks* m_pCallbacks;
NimBLEService* m_pService;
NimBLEAttValue m_value;
std::vector<NimBLEDescriptor*> m_dscVec;
uint8_t m_removed;
std::vector<std::pair<uint16_t, uint16_t>> m_subscribedVec;
NimBLECharacteristicCallbacks* m_pCallbacks{nullptr};
NimBLEService* m_pService{nullptr};
std::vector<NimBLEDescriptor*> m_dscVec{};
std::vector<std::pair<uint16_t, uint16_t>> m_subscribedVec{};
}; // NimBLECharacteristic
/**
* @brief Callbacks that can be associated with a %BLE characteristic to inform of events.
*
@@ -200,14 +136,13 @@ private:
* sub-classed instance of this class and will be notified when such an event happens.
*/
class NimBLECharacteristicCallbacks {
public:
virtual ~NimBLECharacteristicCallbacks(){}
virtual void onRead(NimBLECharacteristic* pCharacteristic, NimBLEConnInfo& connInfo);
virtual void onWrite(NimBLECharacteristic* pCharacteristic, NimBLEConnInfo& connInfo);
virtual void onNotify(NimBLECharacteristic* pCharacteristic);
public:
virtual ~NimBLECharacteristicCallbacks() {}
virtual void onRead(NimBLECharacteristic* pCharacteristic, const NimBLEConnInfo& connInfo);
virtual void onWrite(NimBLECharacteristic* pCharacteristic, const NimBLEConnInfo& connInfo);
virtual void onStatus(NimBLECharacteristic* pCharacteristic, int code);
virtual void onSubscribe(NimBLECharacteristic* pCharacteristic, NimBLEConnInfo& connInfo, uint16_t subValue);
virtual void onSubscribe(NimBLECharacteristic* pCharacteristic, const NimBLEConnInfo& connInfo, uint16_t subValue);
};
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL */
#endif /*MAIN_NIMBLECHARACTERISTIC_H_*/
#endif /*NIMBLE_CPP_CHARACTERISTIC_H_*/

View File

@@ -15,6 +15,8 @@
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
#include "NimBLEClient.h"
#include "NimBLERemoteService.h"
#include "NimBLERemoteCharacteristic.h"
#include "NimBLEDevice.h"
#include "NimBLELog.h"
@@ -111,7 +113,7 @@ NimBLEClient::~NimBLEClient() {
*/
void NimBLEClient::dcTimerCb(ble_npl_event *event) {
/* NimBLEClient *pClient = (NimBLEClient*)event->arg;
NIMBLE_LOGC(LOG_TAG, "Timed out disconnecting from %s - resetting host",
NIMBLE_LOGE(LOG_TAG, "Timed out disconnecting from %s - resetting host",
std::string(pClient->getPeerAddress()).c_str());
*/
ble_hs_sched_reset(BLE_HS_ECONTROLLER);
@@ -189,28 +191,24 @@ bool NimBLEClient::connect(const NimBLEAddress &address, bool deleteAttributes)
NIMBLE_LOGD(LOG_TAG, ">> connect(%s)", address.toString().c_str());
if(!NimBLEDevice::m_synced) {
NIMBLE_LOGC(LOG_TAG, "Host reset, wait for sync.");
NIMBLE_LOGE(LOG_TAG, "Host reset, wait for sync.");
return false;
}
ble_gap_conn_desc desc;
if(ble_gap_conn_find(m_conn_id, &desc) == 0 && (isConnected() || m_connEstablished || m_pTaskData != nullptr)) {
if(isConnected() || m_connEstablished || m_pTaskData != nullptr) {
NIMBLE_LOGE(LOG_TAG, "Client busy, connected to %s, id=%d",
std::string(m_peerAddress).c_str(), getConnId());
return false;
}
ble_addr_t peerAddr_t;
memcpy(&peerAddr_t.val, address.getNative(),6);
peerAddr_t.type = address.getType();
if(ble_gap_conn_find_by_addr(&peerAddr_t, NULL) == 0) {
const ble_addr_t* peerAddr = address.getBase();
if(ble_gap_conn_find_by_addr(peerAddr, NULL) == 0) {
NIMBLE_LOGE(LOG_TAG, "A connection to %s already exists",
address.toString().c_str());
return false;
}
if(address == NimBLEAddress("")) {
if(address.isNull()) {
NIMBLE_LOGE(LOG_TAG, "Invalid peer address;(NULL)");
return false;
} else {
@@ -229,7 +227,7 @@ bool NimBLEClient::connect(const NimBLEAddress &address, bool deleteAttributes)
do {
#if CONFIG_BT_NIMBLE_EXT_ADV
rc = ble_gap_ext_connect(NimBLEDevice::m_own_addr_type,
&peerAddr_t,
peerAddr,
m_connectTimeout,
m_phyMask,
&m_pConnParams,
@@ -239,7 +237,7 @@ bool NimBLEClient::connect(const NimBLEAddress &address, bool deleteAttributes)
this);
#else
rc = ble_gap_connect(NimBLEDevice::m_own_addr_type, &peerAddr_t,
rc = ble_gap_connect(NimBLEDevice::m_own_addr_type, peerAddr,
m_connectTimeout, &m_pConnParams,
NimBLEClient::handleGapEvent, this);
#endif
@@ -337,10 +335,10 @@ bool NimBLEClient::connect(const NimBLEAddress &address, bool deleteAttributes)
* Called automatically when a characteristic or descriptor requires encryption or authentication to access it.
* @return True on success.
*/
bool NimBLEClient::secureConnection() {
bool NimBLEClient::secureConnection() const {
NIMBLE_LOGD(LOG_TAG, ">> secureConnection()");
TaskHandle_t cur_task = xTaskGetCurrentTaskHandle();
ble_task_data_t taskData = {this, cur_task, 0, nullptr};
ble_task_data_t taskData = {const_cast<NimBLEClient*>(this), cur_task, 0, nullptr};
int retryCount = 1;
@@ -460,9 +458,6 @@ void NimBLEClient::setConnectionParams(uint16_t minInterval, uint16_t maxInterva
// These are not used by NimBLE at this time - Must leave at defaults
//m_pConnParams->min_ce_len = minConnTime; // Minimum length of connection event in 0.625ms units
//m_pConnParams->max_ce_len = maxConnTime; // Maximum length of connection event in 0.625ms units
int rc = NimBLEUtils::checkConnParams(&m_pConnParams);
assert(rc == 0 && "Invalid Connection parameters");
} // setConnectionParams
@@ -549,7 +544,7 @@ void NimBLEClient::setConnectTimeout(uint32_t time) {
* @brief Get the connection id for this client.
* @return The connection id.
*/
uint16_t NimBLEClient::getConnId() {
uint16_t NimBLEClient::getConnId() const {
return m_conn_id;
} // getConnId
@@ -562,7 +557,7 @@ uint16_t NimBLEClient::getConnId() {
void NimBLEClient::clearConnection() {
m_conn_id = BLE_HS_CONN_HANDLE_NONE;
m_connEstablished = false;
m_peerAddress = NimBLEAddress();
m_peerAddress = NimBLEAddress{};
} // clearConnection
/**
@@ -617,7 +612,7 @@ bool NimBLEClient::setConnection(uint16_t conn_id) {
/**
* @brief Retrieve the address of the peer.
*/
NimBLEAddress NimBLEClient::getPeerAddress() {
NimBLEAddress NimBLEClient::getPeerAddress() const {
return m_peerAddress;
} // getPeerAddress
@@ -783,7 +778,7 @@ bool NimBLEClient::discoverAttributes() {
return false;
}
for(auto chr: svc->m_characteristicVector) {
for(auto chr: svc->m_vChars) {
if (!chr->retrieveDescriptors()) {
return false;
}
@@ -799,7 +794,7 @@ bool NimBLEClient::discoverAttributes() {
* Here we ask the server for its set of services and wait until we have received them all.
* @return true on success otherwise false if an error occurred
*/
bool NimBLEClient::retrieveServices(const NimBLEUUID *uuid_filter) {
bool NimBLEClient::retrieveServices(const NimBLEUUID *uuidFilter) {
/**
* Design
* ------
@@ -818,10 +813,10 @@ bool NimBLEClient::retrieveServices(const NimBLEUUID *uuid_filter) {
TaskHandle_t cur_task = xTaskGetCurrentTaskHandle();
ble_task_data_t taskData = {this, cur_task, 0, nullptr};
if(uuid_filter == nullptr) {
if(uuidFilter == nullptr) {
rc = ble_gattc_disc_all_svcs(m_conn_id, NimBLEClient::serviceDiscoveredCB, &taskData);
} else {
rc = ble_gattc_disc_svc_by_uuid(m_conn_id, &uuid_filter->getNative()->u,
rc = ble_gattc_disc_svc_by_uuid(m_conn_id, uuidFilter->getBase(),
NimBLEClient::serviceDiscoveredCB, &taskData);
}
@@ -979,7 +974,7 @@ NimBLERemoteCharacteristic* NimBLEClient::getCharacteristic(const uint16_t handl
* @brief Get the current mtu of this connection.
* @returns The MTU value.
*/
uint16_t NimBLEClient::getMTU() {
uint16_t NimBLEClient::getMTU() const {
return ble_att_mtu(m_conn_id);
} // getMTU
@@ -1007,7 +1002,7 @@ int NimBLEClient::handleGapEvent(struct ble_gap_event *event, void *arg) {
case BLE_HS_ETIMEOUT_HCI:
case BLE_HS_ENOTSYNCED:
case BLE_HS_EOS:
NIMBLE_LOGC(LOG_TAG, "Disconnect - host reset, rc=%d", rc);
NIMBLE_LOGE(LOG_TAG, "Disconnect - host reset, rc=%d", rc);
NimBLEDevice::onReset(rc);
break;
default:
@@ -1093,7 +1088,7 @@ int NimBLEClient::handleGapEvent(struct ble_gap_event *event, void *arg) {
continue;
}
auto cVector = &it->m_characteristicVector;
auto cVector = &it->m_vChars;
NIMBLE_LOGD(LOG_TAG, "checking service %s for handle: %d",
it->getUUID().toString().c_str(),
event->notify_rx.attr_handle);

View File

@@ -23,15 +23,14 @@
#include "NimBLEConnInfo.h"
#include "NimBLEAttValue.h"
#include "NimBLEAdvertisedDevice.h"
#include "NimBLERemoteService.h"
#include <vector>
#include <string>
class NimBLERemoteService;
class NimBLERemoteCharacteristic;
class NimBLEClientCallbacks;
class NimBLEAdvertisedDevice;
class NimBLEClientCallbacks;
/**
* @brief A model of a %BLE client.
@@ -42,7 +41,7 @@ public:
bool connect(const NimBLEAddress &address, bool deleteAttributes = true);
bool connect(bool deleteAttributes = true);
int disconnect(uint8_t reason = BLE_ERR_REM_USER_CONN_TERM);
NimBLEAddress getPeerAddress();
NimBLEAddress getPeerAddress() const;
void setPeerAddress(const NimBLEAddress &address);
int getRssi();
std::vector<NimBLERemoteService*>* getServices(bool refresh = false);
@@ -60,12 +59,12 @@ public:
void setClientCallbacks(NimBLEClientCallbacks *pClientCallbacks,
bool deleteCallbacks = true);
std::string toString();
uint16_t getConnId();
uint16_t getConnId() const;
void clearConnection();
bool setConnection(NimBLEConnInfo &conn_info);
bool setConnection(uint16_t conn_id);
uint16_t getMTU();
bool secureConnection();
uint16_t getMTU() const;
bool secureConnection() const;
void setConnectTimeout(uint32_t timeout);
void setConnectionParams(uint16_t minInterval, uint16_t maxInterval,
uint16_t latency, uint16_t timeout,
@@ -93,17 +92,17 @@ private:
const struct ble_gatt_svc *service,
void *arg);
static void dcTimerCb(ble_npl_event *event);
bool retrieveServices(const NimBLEUUID *uuid_filter = nullptr);
bool retrieveServices(const NimBLEUUID *uuidFilter = nullptr);
NimBLEAddress m_peerAddress;
int m_lastErr;
uint16_t m_conn_id;
bool m_connEstablished;
bool m_deleteCallbacks;
int32_t m_connectTimeout;
NimBLEClientCallbacks* m_pClientCallbacks;
ble_task_data_t* m_pTaskData;
ble_npl_callout m_dcTimer;
NimBLEAddress m_peerAddress;
mutable int m_lastErr;
uint16_t m_conn_id;
bool m_connEstablished;
bool m_deleteCallbacks;
int32_t m_connectTimeout;
NimBLEClientCallbacks* m_pClientCallbacks;
mutable ble_task_data_t* m_pTaskData;
ble_npl_callout m_dcTimer;
#if CONFIG_BT_NIMBLE_EXT_ADV
uint8_t m_phyMask;
#endif

View File

@@ -1,6 +1,12 @@
#ifndef NIMBLECONNINFO_H_
#define NIMBLECONNINFO_H_
#if defined(CONFIG_NIMBLE_CPP_IDF)
#include "host/ble_gap.h"
#else
#include "nimble/nimble/host/include/host/ble_gap.h"
#endif
#include "NimBLEAddress.h"
/**

View File

@@ -15,17 +15,24 @@
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#include "NimBLEService.h"
#include "NimBLEDescriptor.h"
#include "NimBLELog.h"
# include "NimBLEService.h"
# include "NimBLEDescriptor.h"
# include "NimBLELog.h"
#include <string>
# include <string>
#define NULL_HANDLE (0xffff)
static const char* LOG_TAG = "NimBLEDescriptor";
static const char* LOG_TAG = "NimBLEDescriptor";
static NimBLEDescriptorCallbacks defaultCallbacks;
/**
* @brief Construct a descriptor
* @param [in] uuid - UUID (const char*) for the descriptor.
* @param [in] properties - Properties for the descriptor.
* @param [in] max_len - The maximum length in bytes that the descriptor value can hold. (Default: 512 bytes for esp32, 20 for all others).
* @param [in] pCharacteristic - pointer to the characteristic instance this descriptor belongs to.
*/
NimBLEDescriptor::NimBLEDescriptor(const char* uuid, uint16_t properties, uint16_t max_len, const NimBLECharacteristic* pCharacteristic)
: NimBLEDescriptor(NimBLEUUID(uuid), properties, max_len, pCharacteristic) {}
/**
* @brief Construct a descriptor
@@ -34,273 +41,110 @@ static NimBLEDescriptorCallbacks defaultCallbacks;
* @param [in] max_len - The maximum length in bytes that the descriptor value can hold. (Default: 512 bytes for esp32, 20 for all others).
* @param [in] pCharacteristic - pointer to the characteristic instance this descriptor belongs to.
*/
NimBLEDescriptor::NimBLEDescriptor(const char* uuid, uint16_t properties, uint16_t max_len,
NimBLECharacteristic* pCharacteristic)
: NimBLEDescriptor(NimBLEUUID(uuid), properties, max_len, pCharacteristic) {
}
/**
* @brief Construct a descriptor
* @param [in] uuid - UUID (const char*) for the descriptor.
* @param [in] properties - Properties for the descriptor.
* @param [in] max_len - The maximum length in bytes that the descriptor value can hold. (Default: 512 bytes for esp32, 20 for all others).
* @param [in] pCharacteristic - pointer to the characteristic instance this descriptor belongs to.
*/
NimBLEDescriptor::NimBLEDescriptor(NimBLEUUID uuid, uint16_t properties, uint16_t max_len,
NimBLECharacteristic* pCharacteristic)
: m_value(std::min(CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH , (int)max_len), max_len) {
m_uuid = uuid;
m_handle = NULL_HANDLE; // Handle is initially unknown.
m_pCharacteristic = pCharacteristic;
m_pCallbacks = &defaultCallbacks; // No initial callback.
m_properties = 0;
m_removed = 0;
if (properties & BLE_GATT_CHR_F_READ) { // convert uint16_t properties to uint8_t
m_properties |= BLE_ATT_F_READ;
}
if (properties & (BLE_GATT_CHR_F_WRITE_NO_RSP | BLE_GATT_CHR_F_WRITE)) {
m_properties |= BLE_ATT_F_WRITE;
}
if (properties & BLE_GATT_CHR_F_READ_ENC) {
m_properties |= BLE_ATT_F_READ_ENC;
}
if (properties & BLE_GATT_CHR_F_READ_AUTHEN) {
m_properties |= BLE_ATT_F_READ_AUTHEN;
}
if (properties & BLE_GATT_CHR_F_READ_AUTHOR) {
m_properties |= BLE_ATT_F_READ_AUTHOR;
}
if (properties & BLE_GATT_CHR_F_WRITE_ENC) {
m_properties |= BLE_ATT_F_WRITE_ENC;
}
if (properties & BLE_GATT_CHR_F_WRITE_AUTHEN) {
m_properties |= BLE_ATT_F_WRITE_AUTHEN;
}
if (properties & BLE_GATT_CHR_F_WRITE_AUTHOR) {
m_properties |= BLE_ATT_F_WRITE_AUTHOR;
NimBLEDescriptor::NimBLEDescriptor(const NimBLEUUID& uuid,
uint16_t properties,
uint16_t max_len,
const NimBLECharacteristic* pCharacteristic)
: NimBLELocalValueAttribute{uuid, 0, max_len}, m_pCallbacks{&defaultCallbacks}, m_pCharacteristic{pCharacteristic} {
// Check if this is the client configuration descriptor and set to removed if true.
if (uuid == NimBLEUUID((uint16_t)0x2902)) {
NIMBLE_LOGW(LOG_TAG, "Manually created 2902 descriptor has no functionality; please remove.");
setRemoved(NIMBLE_ATT_REMOVE_HIDE);
}
// convert uint16_t properties to uint8_t for descriptor properties
uint8_t descProperties = 0;
if (properties & NIMBLE_PROPERTY::READ) {
descProperties |= BLE_ATT_F_READ;
}
if (properties & (NIMBLE_PROPERTY::WRITE_NR | NIMBLE_PROPERTY::WRITE)) {
descProperties |= BLE_ATT_F_WRITE;
}
if (properties & NIMBLE_PROPERTY::READ_ENC) {
descProperties |= BLE_ATT_F_READ_ENC;
}
if (properties & NIMBLE_PROPERTY::READ_AUTHEN) {
descProperties |= BLE_ATT_F_READ_AUTHEN;
}
if (properties & NIMBLE_PROPERTY::READ_AUTHOR) {
descProperties |= BLE_ATT_F_READ_AUTHOR;
}
if (properties & NIMBLE_PROPERTY::WRITE_ENC) {
descProperties |= BLE_ATT_F_WRITE_ENC;
}
if (properties & NIMBLE_PROPERTY::WRITE_AUTHEN) {
descProperties |= BLE_ATT_F_WRITE_AUTHEN;
}
if (properties & NIMBLE_PROPERTY::WRITE_AUTHOR) {
descProperties |= BLE_ATT_F_WRITE_AUTHOR;
}
setProperties(descProperties);
} // NimBLEDescriptor
/**
* @brief NimBLEDescriptor destructor.
*/
NimBLEDescriptor::~NimBLEDescriptor() {
} // ~NimBLEDescriptor
/**
* @brief Get the BLE handle for this descriptor.
* @return The handle for this descriptor.
*/
uint16_t NimBLEDescriptor::getHandle() {
return m_handle;
} // getHandle
/**
* @brief Get the length of the value of this descriptor.
* @return The length (in bytes) of the value of this descriptor.
*/
size_t NimBLEDescriptor::getLength() {
return m_value.size();
} // getLength
/**
* @brief Get the UUID of the descriptor.
*/
NimBLEUUID NimBLEDescriptor::getUUID() {
return m_uuid;
} // getUUID
/**
* @brief Get the value of this descriptor.
* @return The NimBLEAttValue of this descriptor.
*/
NimBLEAttValue NimBLEDescriptor::getValue(time_t *timestamp) {
if (timestamp != nullptr) {
m_value.getValue(timestamp);
}
return m_value;
} // getValue
/**
* @brief Get the value of this descriptor as a string.
* @return A std::string instance containing a copy of the descriptor's value.
*/
std::string NimBLEDescriptor::getStringValue() {
return std::string(m_value);
}
/**
* @brief Get the characteristic this descriptor belongs to.
* @return A pointer to the characteristic this descriptor belongs to.
*/
NimBLECharacteristic* NimBLEDescriptor::getCharacteristic() {
const NimBLECharacteristic* NimBLEDescriptor::getCharacteristic() const {
return m_pCharacteristic;
} // getCharacteristic
int NimBLEDescriptor::handleGapEvent(uint16_t conn_handle, uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt, void *arg) {
(void)conn_handle;
(void)attr_handle;
const ble_uuid_t *uuid;
int rc;
NimBLEConnInfo peerInfo;
NimBLEDescriptor* pDescriptor = (NimBLEDescriptor*)arg;
NIMBLE_LOGD(LOG_TAG, "Descriptor %s %s event", pDescriptor->getUUID().toString().c_str(),
ctxt->op == BLE_GATT_ACCESS_OP_READ_DSC ? "Read" : "Write");
uuid = ctxt->chr->uuid;
if(ble_uuid_cmp(uuid, &pDescriptor->getUUID().getNative()->u) == 0){
switch(ctxt->op) {
case BLE_GATT_ACCESS_OP_READ_DSC: {
rc = ble_gap_conn_find(conn_handle, &peerInfo.m_desc);
assert(rc == 0);
// If the packet header is only 8 bytes this is a follow up of a long read
// so we don't want to call the onRead() callback again.
if(ctxt->om->om_pkthdr_len > 8 ||
pDescriptor->m_value.size() <= (ble_att_mtu(peerInfo.getConnHandle()) - 3)) {
pDescriptor->m_pCallbacks->onRead(pDescriptor, peerInfo);
}
ble_npl_hw_enter_critical();
rc = os_mbuf_append(ctxt->om, pDescriptor->m_value.data(), pDescriptor->m_value.size());
ble_npl_hw_exit_critical(0);
return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
}
case BLE_GATT_ACCESS_OP_WRITE_DSC: {
rc = ble_gap_conn_find(conn_handle, &peerInfo.m_desc);
assert(rc == 0);
uint16_t att_max_len = pDescriptor->m_value.max_size();
if (ctxt->om->om_len > att_max_len) {
return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
}
uint8_t buf[att_max_len];
size_t len = ctxt->om->om_len;
memcpy(buf, ctxt->om->om_data,len);
os_mbuf *next;
next = SLIST_NEXT(ctxt->om, om_next);
while(next != NULL){
if((len + next->om_len) > att_max_len) {
return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
}
memcpy(&buf[len], next->om_data, next->om_len);
len += next->om_len;
next = SLIST_NEXT(next, om_next);
}
pDescriptor->setValue(buf, len);
pDescriptor->m_pCallbacks->onWrite(pDescriptor, peerInfo);
return 0;
}
default:
break;
}
}
return BLE_ATT_ERR_UNLIKELY;
}
/**
* @brief Set the callback handlers for this descriptor.
* @param [in] pCallbacks An instance of a callback structure used to define any callbacks for the descriptor.
*/
void NimBLEDescriptor::setCallbacks(NimBLEDescriptorCallbacks* pCallbacks) {
if (pCallbacks != nullptr){
if (pCallbacks != nullptr) {
m_pCallbacks = pCallbacks;
} else {
m_pCallbacks = &defaultCallbacks;
}
} // setCallbacks
/**
* @brief Set the handle of this descriptor.
* Set the handle of this descriptor to be the supplied value.
* @param [in] handle The handle to be associated with this descriptor.
* @return N/A.
*/
void NimBLEDescriptor::setHandle(uint16_t handle) {
NIMBLE_LOGD(LOG_TAG, ">> setHandle(0x%.2x): Setting descriptor handle to be 0x%.2x", handle, handle);
m_handle = handle;
NIMBLE_LOGD(LOG_TAG, "<< setHandle()");
} // setHandle
/**
* @brief Set the value of the descriptor.
* @param [in] data The data to set for the descriptor.
* @param [in] length The length of the data in bytes.
*/
void NimBLEDescriptor::setValue(const uint8_t* data, size_t length) {
m_value.setValue(data, length);
} // setValue
/**
* @brief Set the value of the descriptor from a `std::vector<uint8_t>`.\n
* @param [in] vec The std::vector<uint8_t> reference to set the descriptor value from.
*/
void NimBLEDescriptor::setValue(const std::vector<uint8_t>& vec) {
return setValue((uint8_t*)&vec[0], vec.size());
} // setValue
/**
* @brief Set the characteristic this descriptor belongs to.
* @param [in] pChar A pointer to the characteristic this descriptor belongs to.
*/
void NimBLEDescriptor::setCharacteristic(NimBLECharacteristic* pChar) {
void NimBLEDescriptor::setCharacteristic(const NimBLECharacteristic* pChar) {
m_pCharacteristic = pChar;
} // setCharacteristic
/**
* @brief Return a string representation of the descriptor.
* @return A string representation of the descriptor.
*/
std::string NimBLEDescriptor::toString() {
std::string NimBLEDescriptor::toString() const {
char hex[5];
snprintf(hex, sizeof(hex), "%04x", m_handle);
snprintf(hex, sizeof(hex), "%04x", getHandle());
std::string res = "UUID: " + m_uuid.toString() + ", handle: 0x" + hex;
return res;
} // toString
void NimBLEDescriptor::readEvent(const NimBLEConnInfo& connInfo) {
m_pCallbacks->onRead(this, connInfo);
} // readEvent
void NimBLEDescriptor::writeEvent(const uint8_t* val, uint16_t len, const NimBLEConnInfo& connInfo) {
setValue(val, len);
m_pCallbacks->onWrite(this, connInfo);
} // writeEvent
/**
* @brief Callback function to support a read request.
* @param [in] pDescriptor The descriptor that is the source of the event.
* @param [in] connInfo A reference to a NimBLEConnInfo instance containing the peer info.
*/
void NimBLEDescriptorCallbacks::onRead(NimBLEDescriptor* pDescriptor, NimBLEConnInfo& connInfo) {
(void)pDescriptor;
void NimBLEDescriptorCallbacks::onRead(NimBLEDescriptor* pDescriptor, const NimBLEConnInfo& connInfo) {
NIMBLE_LOGD("NimBLEDescriptorCallbacks", "onRead: default");
} // onRead
/**
* @brief Callback function to support a write request.
* @param [in] pDescriptor The descriptor that is the source of the event.
* @param [in] connInfo A reference to a NimBLEConnInfo instance containing the peer info.
*/
void NimBLEDescriptorCallbacks::onWrite(NimBLEDescriptor* pDescriptor, NimBLEConnInfo& connInfo) {
(void)pDescriptor;
void NimBLEDescriptorCallbacks::onWrite(NimBLEDescriptor* pDescriptor, const NimBLEConnInfo& connInfo) {
NIMBLE_LOGD("NimBLEDescriptorCallbacks", "onWrite: default");
} // onWrite

View File

@@ -12,94 +12,55 @@
* Author: kolban
*/
#ifndef MAIN_NIMBLEDESCRIPTOR_H_
#define MAIN_NIMBLEDESCRIPTOR_H_
#ifndef NIMBLE_CPP_DESCRIPTOR_H_
#define NIMBLE_CPP_DESCRIPTOR_H_
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#include "NimBLECharacteristic.h"
#include "NimBLEUUID.h"
#include "NimBLEAttValue.h"
#include "NimBLEConnInfo.h"
#include <string>
class NimBLEService;
class NimBLECharacteristic;
class NimBLEDescriptor;
class NimBLEDescriptorCallbacks;
# include "NimBLELocalValueAttribute.h"
# include "NimBLECharacteristic.h"
# include "NimBLEUUID.h"
# include "NimBLEAttValue.h"
# include "NimBLEConnInfo.h"
# include <string>
/**
* @brief A model of a %BLE descriptor.
* @brief A model of a BLE descriptor.
*/
class NimBLEDescriptor {
public:
NimBLEDescriptor(const char* uuid, uint16_t properties,
uint16_t max_len,
NimBLECharacteristic* pCharacteristic = nullptr);
class NimBLEDescriptor : public NimBLELocalValueAttribute {
public:
NimBLEDescriptor(const char* uuid,
uint16_t properties,
uint16_t max_len,
const NimBLECharacteristic* pCharacteristic = nullptr);
NimBLEDescriptor(NimBLEUUID uuid, uint16_t properties,
uint16_t max_len,
NimBLECharacteristic* pCharacteristic = nullptr);
NimBLEDescriptor(const NimBLEUUID& uuid,
uint16_t properties,
uint16_t max_len,
const NimBLECharacteristic* pCharacteristic = nullptr);
~NimBLEDescriptor() = default;
~NimBLEDescriptor();
std::string toString() const;
void setCallbacks(NimBLEDescriptorCallbacks* pCallbacks);
const NimBLECharacteristic* getCharacteristic() const;
uint16_t getHandle();
NimBLEUUID getUUID();
std::string toString();
void setCallbacks(NimBLEDescriptorCallbacks* pCallbacks);
NimBLECharacteristic* getCharacteristic();
size_t getLength();
NimBLEAttValue getValue(time_t *timestamp = nullptr);
std::string getStringValue();
void setValue(const uint8_t* data, size_t size);
void setValue(const std::vector<uint8_t>& vec);
/*********************** Template Functions ************************/
/**
* @brief Template to set the characteristic value to <type\>val.
* @param [in] s The value to set.
*/
template<typename T>
void setValue(const T &s) { m_value.setValue<T>(s); }
/**
* @brief Template to convert the descriptor data to <type\>.
* @tparam T The type to convert the data to.
* @param [in] timestamp (Optional) A pointer to a time_t struct to store the time the value was read.
* @param [in] skipSizeCheck (Optional) If true it will skip checking if the data size is less than <tt>sizeof(<type\>)</tt>.
* @return The data converted to <type\> or NULL if skipSizeCheck is false and the data is less than <tt>sizeof(<type\>)</tt>.
* @details <b>Use:</b> <tt>getValue<type>(&timestamp, skipSizeCheck);</tt>
*/
template<typename T>
T getValue(time_t *timestamp = nullptr, bool skipSizeCheck = false) {
return m_value.getValue<T>(timestamp, skipSizeCheck);
}
private:
private:
friend class NimBLECharacteristic;
friend class NimBLEService;
friend class NimBLE2904;
static int handleGapEvent(uint16_t conn_handle, uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt, void *arg);
void setHandle(uint16_t handle);
void setCharacteristic(NimBLECharacteristic* pChar);
void setCharacteristic(const NimBLECharacteristic* pChar);
void readEvent(const NimBLEConnInfo& connInfo) override;
void writeEvent(const uint8_t* val, uint16_t len, const NimBLEConnInfo& connInfo) override;
NimBLEUUID m_uuid;
uint16_t m_handle;
NimBLEDescriptorCallbacks* m_pCallbacks;
NimBLECharacteristic* m_pCharacteristic;
uint8_t m_properties;
NimBLEAttValue m_value;
uint8_t m_removed;
NimBLEDescriptorCallbacks* m_pCallbacks{nullptr};
const NimBLECharacteristic* m_pCharacteristic{nullptr};
}; // NimBLEDescriptor
/**
* @brief Callbacks that can be associated with a %BLE descriptors to inform of events.
*
@@ -108,13 +69,13 @@ private:
* sub-classed instance of this class and will be notified when such an event happens.
*/
class NimBLEDescriptorCallbacks {
public:
virtual ~NimBLEDescriptorCallbacks(){}
virtual void onRead(NimBLEDescriptor* pDescriptor, NimBLEConnInfo& connInfo);
virtual void onWrite(NimBLEDescriptor* pDescriptor, NimBLEConnInfo& connInfo);
public:
virtual ~NimBLEDescriptorCallbacks() = default;
virtual void onRead(NimBLEDescriptor* pDescriptor, const NimBLEConnInfo& connInfo);
virtual void onWrite(NimBLEDescriptor* pDescriptor, const NimBLEConnInfo& connInfo);
};
#include "NimBLE2904.h"
# include "NimBLE2904.h"
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL */
#endif /* MAIN_NIMBLEDESCRIPTOR_H_ */
#endif /* NIMBLE_CPP_DESCRIPTOR_H_ */

View File

@@ -54,7 +54,12 @@
# include "esp32-hal-bt.h"
#endif
#if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
#include "NimBLEClient.h"
#endif
#include "NimBLELog.h"
#include <algorithm>
static const char* LOG_TAG = "NimBLEDevice";
@@ -78,12 +83,13 @@ NimBLEAdvertising* NimBLEDevice::m_bleAdvertising = nullptr;
# endif
#endif
#if defined( CONFIG_BT_NIMBLE_ROLE_CENTRAL)
std::array<NimBLEClient*, NIMBLE_MAX_CONNECTIONS> NimBLEDevice::m_pClients{nullptr};
#endif
gap_event_handler NimBLEDevice::m_customGapHandler = nullptr;
ble_gap_event_listener NimBLEDevice::m_listener;
#if defined( CONFIG_BT_NIMBLE_ROLE_CENTRAL)
std::list <NimBLEClient*> NimBLEDevice::m_cList;
#endif
std::list <NimBLEAddress> NimBLEDevice::m_ignoreList;
std::vector <NimBLEAddress> NimBLEDevice::m_ignoreList;
std::vector<NimBLEAddress> NimBLEDevice::m_whiteList;
uint8_t NimBLEDevice::m_own_addr_type = BLE_OWN_ADDR_PUBLIC;
#ifdef ESP_PLATFORM
@@ -213,18 +219,23 @@ NimBLEScan* NimBLEDevice::getScan() {
* each client can connect to 1 peripheral device.
* @param [in] peerAddress An optional peer address that is copied to the new client
* object, allows for calling NimBLEClient::connect(bool) without a device or address parameter.
* @return A reference to the new client object.
* @return A reference to the new client object, or nullptr on error.
*/
#if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
/* STATIC */
NimBLEClient* NimBLEDevice::createClient(NimBLEAddress peerAddress) {
if(m_cList.size() >= NIMBLE_MAX_CONNECTIONS) {
NIMBLE_LOGW(LOG_TAG,"Number of clients exceeds Max connections. Cur=%d Max=%d",
m_cList.size(), NIMBLE_MAX_CONNECTIONS);
if (getCreatedClientCount() == NIMBLE_MAX_CONNECTIONS) {
NIMBLE_LOGE(LOG_TAG,"Unable to create client; already at max: %d",NIMBLE_MAX_CONNECTIONS);
return nullptr;
}
NimBLEClient* pClient = new NimBLEClient(peerAddress);
m_cList.push_back(pClient);
for (auto& clt : m_pClients) {
if (clt == nullptr) {
clt = pClient;
break;
}
}
return pClient;
} // createClient
@@ -269,46 +280,47 @@ bool NimBLEDevice::deleteClient(NimBLEClient* pClient) {
}
}
m_cList.remove(pClient);
delete pClient;
for (auto& clt : m_pClients) {
if (clt == pClient) {
delete pClient;
clt = nullptr;
}
}
return true;
} // deleteClient
/**
* @brief Get the list of created client objects.
* @return A pointer to the list of clients.
*/
/* STATIC */
std::list<NimBLEClient*>* NimBLEDevice::getClientList() {
return &m_cList;
} // getClientList
/**
* @brief Get the number of created client objects.
* @return Number of client objects created.
*/
/* STATIC */
size_t NimBLEDevice::getClientListSize() {
return m_cList.size();
} // getClientList
size_t NimBLEDevice::getCreatedClientCount() {
auto count = 0;
for (auto clt : m_pClients) {
if (clt != nullptr) {
count++;
}
}
return count;
} // getCreatedClientCount
/**
* @brief Get a reference to a client by connection ID.
* @param [in] conn_id The client connection ID to search for.
* @return A pointer to the client object with the spcified connection ID.
* @return A pointer to the client object with the specified connection ID or nullptr.
*/
/* STATIC */
NimBLEClient* NimBLEDevice::getClientByID(uint16_t conn_id) {
for(auto it = m_cList.cbegin(); it != m_cList.cend(); ++it) {
if((*it)->getConnId() == conn_id) {
return (*it);
for(auto clt : m_pClients) {
if(clt != nullptr && clt->getConnId() == conn_id) {
return clt;
}
}
assert(0);
return nullptr;
} // getClientByID
@@ -316,30 +328,32 @@ NimBLEClient* NimBLEDevice::getClientByID(uint16_t conn_id) {
/**
* @brief Get a reference to a client by peer address.
* @param [in] peer_addr The address of the peer to search for.
* @return A pointer to the client object with the peer address.
* @return A pointer to the client object with the peer address or nullptr.
*/
/* STATIC */
NimBLEClient* NimBLEDevice::getClientByPeerAddress(const NimBLEAddress &peer_addr) {
for(auto it = m_cList.cbegin(); it != m_cList.cend(); ++it) {
if((*it)->getPeerAddress().equals(peer_addr)) {
return (*it);
for(auto clt : m_pClients) {
if(clt != nullptr && clt->getPeerAddress() == peer_addr) {
return clt;
}
}
return nullptr;
} // getClientPeerAddress
/**
* @brief Finds the first disconnected client in the list.
* @return A pointer to the first client object that is not connected to a peer.
* @return A pointer to the first client object that is not connected to a peer or nullptr.
*/
/* STATIC */
NimBLEClient* NimBLEDevice::getDisconnectedClient() {
for(auto it = m_cList.cbegin(); it != m_cList.cend(); ++it) {
if(!(*it)->isConnected()) {
return (*it);
for(auto clt : m_pClients) {
if(clt != nullptr && !clt->isConnected()) {
return clt;
}
}
return nullptr;
} // getDisconnectedClient
@@ -444,7 +458,7 @@ int NimBLEDevice::getPower() {
*/
/* STATIC*/
NimBLEAddress NimBLEDevice::getAddress() {
ble_addr_t addr = {BLE_ADDR_PUBLIC, 0};
ble_addr_t addr{};
if(BLE_HS_ENOADDR == ble_hs_id_copy_addr(BLE_ADDR_PUBLIC, addr.val, NULL)) {
NIMBLE_LOGD(LOG_TAG, "Public address not found, checking random");
@@ -587,16 +601,7 @@ bool NimBLEDevice::deleteAllBonds() {
*/
/*STATIC*/
bool NimBLEDevice::deleteBond(const NimBLEAddress &address) {
ble_addr_t delAddr;
memcpy(&delAddr.val, address.getNative(),6);
delAddr.type = address.getType();
int rc = ble_gap_unpair(&delAddr);
if (rc != 0) {
return false;
}
return true;
return ble_gap_unpair(address.getBase()) == 0;
}
@@ -673,26 +678,14 @@ bool NimBLEDevice::onWhiteList(const NimBLEAddress & address) {
*/
/*STATIC*/
bool NimBLEDevice::whiteListAdd(const NimBLEAddress & address) {
if (NimBLEDevice::onWhiteList(address)) {
return true;
}
m_whiteList.push_back(address);
std::vector<ble_addr_t> wlVec;
wlVec.reserve(m_whiteList.size());
for (auto &it : m_whiteList) {
ble_addr_t wlAddr;
memcpy(&wlAddr.val, it.getNative(), 6);
wlAddr.type = it.getType();
wlVec.push_back(wlAddr);
}
int rc = ble_gap_wl_set(&wlVec[0], wlVec.size());
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "Failed adding to whitelist rc=%d", rc);
m_whiteList.pop_back();
return false;
if (!NimBLEDevice::onWhiteList(address)) {
m_whiteList.push_back(address);
int rc = ble_gap_wl_set(reinterpret_cast<ble_addr_t*>(&m_whiteList[0]), m_whiteList.size());
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "Failed adding to whitelist rc=%d", rc);
m_whiteList.pop_back();
return false;
}
}
return true;
@@ -706,33 +699,14 @@ bool NimBLEDevice::whiteListAdd(const NimBLEAddress & address) {
*/
/*STATIC*/
bool NimBLEDevice::whiteListRemove(const NimBLEAddress & address) {
if (!NimBLEDevice::onWhiteList(address)) {
return true;
}
std::vector<ble_addr_t> wlVec;
wlVec.reserve(m_whiteList.size());
for (auto &it : m_whiteList) {
if (it != address) {
ble_addr_t wlAddr;
memcpy(&wlAddr.val, it.getNative(), 6);
wlAddr.type = it.getType();
wlVec.push_back(wlAddr);
}
}
int rc = ble_gap_wl_set(&wlVec[0], wlVec.size());
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "Failed removing from whitelist rc=%d", rc);
return false;
}
// Don't remove from the list unless NimBLE returned success
for (auto it = m_whiteList.begin(); it < m_whiteList.end(); ++it) {
if ((*it) == address) {
m_whiteList.erase(it);
break;
auto it = std::find(m_whiteList.begin(), m_whiteList.end(), address);
if (it != m_whiteList.end()) {
m_whiteList.erase(it);
int rc = ble_gap_wl_set(reinterpret_cast<ble_addr_t*>(&m_whiteList[0]), m_whiteList.size());
if (rc != 0) {
m_whiteList.push_back(address);
NIMBLE_LOGE(LOG_TAG, "Failed removing from whitelist rc=%d", rc);
return false;
}
}
@@ -778,7 +752,7 @@ void NimBLEDevice::onReset(int reason)
m_synced = false;
NIMBLE_LOGC(LOG_TAG, "Resetting state; reason=%d, %s", reason,
NIMBLE_LOGE(LOG_TAG, "Resetting state; reason=%d, %s", reason,
NimBLEUtils::returnCodeToString(reason));
#if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
@@ -806,7 +780,10 @@ void NimBLEDevice::onSync(void)
/* Make sure we have proper identity address set (public preferred) */
int rc = ble_hs_util_ensure_addr(0);
assert(rc == 0);
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "error ensuring address; rc=%d", rc);
return;
}
#ifndef ESP_PLATFORM
rc = ble_hs_id_infer_auto(m_own_addr_type, &m_own_addr_type);
@@ -918,7 +895,9 @@ void NimBLEDevice::init(const std::string &deviceName) {
// Set the device name.
rc = ble_svc_gap_device_name_set(deviceName.c_str());
assert(rc == 0);
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "ble_svc_gap_device_name_set() failed; rc=%d", rc);
}
ble_store_config_init();
@@ -978,12 +957,10 @@ void NimBLEDevice::deinit(bool clearAll) {
#endif
#if defined( CONFIG_BT_NIMBLE_ROLE_CENTRAL)
for(auto &it : m_cList) {
deleteClient(it);
m_cList.clear();
for(auto clt : m_pClients) {
deleteClient(clt);
}
#endif
m_ignoreList.clear();
}
}
@@ -1248,10 +1225,22 @@ void NimBLEDevice::setCustomGapHandler(gap_event_handler handler) {
int rc = ble_gap_event_listener_register(&m_listener, m_customGapHandler, NULL);
if(rc == BLE_HS_EALREADY){
NIMBLE_LOGI(LOG_TAG, "Already listening to GAP events.");
} else if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "ble_gap_event_listener_register: rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc));
}
else{
assert(rc == 0);
}
} // setCustomGapHandler
#endif // CONFIG_BT_ENABLED
#if CONFIG_NIMBLE_CPP_DEBUG_ASSERT_ENABLED || __DOXYGEN__
/**
* @brief Debug assert - weak function.
* @param [in] file The file where the assert occurred.
* @param [in] line The line number where the assert occurred.
*/
void nimble_cpp_assert(const char *file, unsigned line) {
NIMBLE_LOGC("", "Assertion failed at %s:%u\n", file, line);
abort();
}
#endif // CONFIG_NIMBLE_CPP_DEBUG_ASSERT_ENABLED
#endif // CONFIG_BT_ENABLED

View File

@@ -31,7 +31,7 @@
#endif
#if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
#include "NimBLEClient.h"
class NimBLEClient;
#endif
#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
@@ -45,9 +45,8 @@
# include "esp_bt.h"
#endif
#include <map>
#include <string>
#include <list>
#include <array>
#define BLEDevice NimBLEDevice
#define BLEClient NimBLEClient
@@ -161,13 +160,12 @@ public:
#endif
#if defined( CONFIG_BT_NIMBLE_ROLE_CENTRAL)
static NimBLEClient* createClient(NimBLEAddress peerAddress = NimBLEAddress(""));
static NimBLEClient* createClient(NimBLEAddress peerAddress = NimBLEAddress{});
static bool deleteClient(NimBLEClient* pClient);
static NimBLEClient* getClientByID(uint16_t conn_id);
static NimBLEClient* getClientByPeerAddress(const NimBLEAddress &peer_addr);
static NimBLEClient* getDisconnectedClient();
static size_t getClientListSize();
static std::list<NimBLEClient*>* getClientList();
static size_t getCreatedClientCount();
#endif
#if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL) || defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
@@ -221,23 +219,30 @@ private:
# endif
#endif
#if defined( CONFIG_BT_NIMBLE_ROLE_CENTRAL)
static std::list <NimBLEClient*> m_cList;
#endif
static std::list <NimBLEAddress> m_ignoreList;
static std::vector<NimBLEAddress> m_ignoreList;
static uint32_t m_passkey;
static ble_gap_event_listener m_listener;
static gap_event_handler m_customGapHandler;
static uint8_t m_own_addr_type;
static std::vector<NimBLEAddress> m_whiteList;
#ifdef ESP_PLATFORM
# ifdef CONFIG_BTDM_BLE_SCAN_DUPL
static uint16_t m_scanDuplicateSize;
static uint8_t m_scanFilterMode;
# endif
#endif
static std::vector<NimBLEAddress> m_whiteList;
#if defined( CONFIG_BT_NIMBLE_ROLE_CENTRAL)
static std::array<NimBLEClient*, NIMBLE_MAX_CONNECTIONS> m_pClients;
#endif
};
#if defined( CONFIG_BT_NIMBLE_ROLE_CENTRAL)
#include "NimBLEClient.h"
#include "NimBLERemoteService.h"
#include "NimBLERemoteCharacteristic.h"
#include "NimBLERemoteDescriptor.h"
#endif
#endif // CONFIG_BT_ENABLED
#endif // MAIN_NIMBLEDEVICE_H_

View File

@@ -176,7 +176,11 @@ void NimBLEEddystoneTLM::setData(const std::string &data) {
* @param [in] l_uuid The UUID.
*/
void NimBLEEddystoneTLM::setUUID(const NimBLEUUID &l_uuid) {
beaconUUID = l_uuid.getNative()->u16.value;
if (l_uuid.bitSize() != 16) {
NIMBLE_LOGE(LOG_TAG, "UUID must be 16 bits");
return;
}
beaconUUID = *reinterpret_cast<const uint16_t*>(l_uuid.getValue());
} // setUUID

View File

@@ -172,7 +172,11 @@ void NimBLEEddystoneURL::setData(const std::string &data) {
* @param [in] l_uuid The UUID.
*/
void NimBLEEddystoneURL::setUUID(const NimBLEUUID &l_uuid) {
beaconUUID = l_uuid.getNative()->u16.value;
if (l_uuid.bitSize() != 16) {
NIMBLE_LOGE(LOG_TAG, "UUID must be 16 bits");
return;
}
beaconUUID = *reinterpret_cast<const uint16_t*>(l_uuid.getValue());
} // setUUID

View File

@@ -100,12 +100,8 @@ bool NimBLEExtAdvertising::setInstanceData(uint8_t inst_id, NimBLEExtAdvertiseme
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "Invalid advertisement data: rc = %d", rc);
} else {
if (adv.m_advAddress != NimBLEAddress("")) {
ble_addr_t addr;
memcpy(&addr.val, adv.m_advAddress.getNative(), 6);
// Custom advertising address must be random.
addr.type = BLE_OWN_ADDR_RANDOM;
rc = ble_gap_ext_adv_set_addr(inst_id, &addr);
if (!adv.m_advAddress.isNull()) {
rc = ble_gap_ext_adv_set_addr(inst_id, adv.m_advAddress.getBase());
}
if (rc != 0) {
@@ -341,7 +337,7 @@ int NimBLEExtAdvertising::handleGapEvent(struct ble_gap_event *event, void *arg)
case BLE_HS_EOS:
case BLE_HS_ECONTROLLER:
case BLE_HS_ENOTSYNCED:
NIMBLE_LOGC(LOG_TAG, "host reset, rc = %d", event->adv_complete.reason);
NIMBLE_LOGE(LOG_TAG, "host reset, rc = %d", event->adv_complete.reason);
NimBLEDevice::onReset(event->adv_complete.reason);
return 0;
default:
@@ -388,7 +384,7 @@ void NimBLEExtAdvertisingCallbacks::onScanRequest(NimBLEExtAdvertising *pAdv,
* * BLE_HCI_LE_PHY_CODED
*/
NimBLEExtAdvertisement::NimBLEExtAdvertisement(uint8_t priPhy, uint8_t secPhy)
: m_advAddress("")
: m_advAddress{}
{
memset (&m_params, 0, sizeof(m_params));
m_params.own_addr_type = NimBLEDevice::m_own_addr_type;
@@ -493,10 +489,7 @@ void NimBLEExtAdvertisement::setScanFilter(bool scanRequestWhitelistOnly, bool c
* @param [in] addr The address of the peer to direct the advertisements.
*/
void NimBLEExtAdvertisement::setDirectedPeer(const NimBLEAddress & addr) {
ble_addr_t peerAddr;
memcpy(&peerAddr.val, addr.getNative(), 6);
peerAddr.type = addr.getType();
m_params.peer = peerAddr;
m_params.peer = *addr.getBase();
} // setDirectedPeer
@@ -768,21 +761,9 @@ void NimBLEExtAdvertisement::setServices(const bool complete, const uint8_t size
for(auto &it : v_uuid){
if(it.bitSize() != size) {
NIMBLE_LOGE(LOG_TAG, "Service UUID(%d) invalid", size);
return;
continue;
} else {
switch(size) {
case 16:
uuids += std::string((char*)&it.getNative()->u16.value, 2);
break;
case 32:
uuids += std::string((char*)&it.getNative()->u32.value, 4);
break;
case 128:
uuids += std::string((char*)&it.getNative()->u128.value, 16);
break;
default:
return;
}
uuids += std::string(reinterpret_cast<const char*>(it.getValue()), size / 8);
}
}
@@ -796,35 +777,30 @@ void NimBLEExtAdvertisement::setServices(const bool complete, const uint8_t size
* @param [in] data The data to be associated with the service data advertised.
*/
void NimBLEExtAdvertisement::setServiceData(const NimBLEUUID &uuid, const std::string &data) {
char cdata[2];
switch (uuid.bitSize()) {
case 16: {
uint8_t size = uuid.bitSize() / 8;
char cdata[2] = {static_cast<char>(1 + size), BLE_HS_ADV_TYPE_SVC_DATA_UUID16};
switch (size) {
case 2: {
// [Len] [0x16] [UUID16] data
cdata[0] = data.length() + 3;
cdata[1] = BLE_HS_ADV_TYPE_SVC_DATA_UUID16; // 0x16
addData(std::string(cdata, 2) + std::string((char*) &uuid.getNative()->u16.value, 2) + data);
break;
}
case 32: {
// [Len] [0x20] [UUID32] data
cdata[0] = data.length() + 5;
cdata[1] = BLE_HS_ADV_TYPE_SVC_DATA_UUID32; // 0x20
addData(std::string(cdata, 2) + std::string((char*) &uuid.getNative()->u32.value, 4) + data);
break;
}
case 128: {
case 16: {
// [Len] [0x21] [UUID128] data
cdata[0] = data.length() + 17;
cdata[1] = BLE_HS_ADV_TYPE_SVC_DATA_UUID128; // 0x21
addData(std::string(cdata, 2) + std::string((char*) &uuid.getNative()->u128.value, 16) + data);
break;
}
case 4: {
// [Len] [0x20] [UUID32] data
cdata[1] = BLE_HS_ADV_TYPE_SVC_DATA_UUID32; // 0x20
break;
}
default:
return;
}
addData(std::string(cdata, 2) + std::string(reinterpret_cast<const char*>(uuid.getValue()), size) + data);
} // setServiceData

View File

@@ -0,0 +1,48 @@
/*
* NimBLELocalAttribute.cpp
*
* Created: on July 28 2024
* Author H2zero
*/
#ifndef NIMBLE_CPP_LOCAL_ATTRIBUTE_H_
#define NIMBLE_CPP_LOCAL_ATTRIBUTE_H_
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
# include "NimBLEAttribute.h"
/**
* @brief A base class for local BLE attributes.
*/
class NimBLELocalAttribute : public NimBLEAttribute {
public:
/**
* @brief Get the removed flag.
* @return The removed flag.
*/
uint8_t getRemoved() const { return m_removed; }
protected:
/**
* @brief Construct a local attribute.
*/
NimBLELocalAttribute(const NimBLEUUID& uuid, uint16_t handle) : NimBLEAttribute{uuid, handle}, m_removed{0} {}
/**
* @brief Destroy the local attribute.
*/
~NimBLELocalAttribute() = default;
/**
* @brief Set the removed flag.
* @param [in] removed The removed flag.
*/
void setRemoved(uint8_t removed) { m_removed = removed; }
uint8_t m_removed{0};
};
#endif // CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL
#endif // NIMBLE_CPP_LOCAL_ATTRIBUTE_H_

View File

@@ -0,0 +1,156 @@
/*
* NimBLELocalValueAttribute.cpp
*
* Created: on July 28 2024
* Author H2zero
*/
#ifndef _NIMBLE_LOCAL_VALUE_ATTRIBUTE_H_
#define _NIMBLE_LOCAL_VALUE_ATTRIBUTE_H_
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
# if defined(CONFIG_NIMBLE_CPP_IDF)
# include "host/ble_hs.h"
# else
# include "nimble/nimble/host/include/host/ble_hs.h"
# endif
typedef enum {
READ = BLE_GATT_CHR_F_READ,
READ_ENC = BLE_GATT_CHR_F_READ_ENC,
READ_AUTHEN = BLE_GATT_CHR_F_READ_AUTHEN,
READ_AUTHOR = BLE_GATT_CHR_F_READ_AUTHOR,
WRITE = BLE_GATT_CHR_F_WRITE,
WRITE_NR = BLE_GATT_CHR_F_WRITE_NO_RSP,
WRITE_ENC = BLE_GATT_CHR_F_WRITE_ENC,
WRITE_AUTHEN = BLE_GATT_CHR_F_WRITE_AUTHEN,
WRITE_AUTHOR = BLE_GATT_CHR_F_WRITE_AUTHOR,
BROADCAST = BLE_GATT_CHR_F_BROADCAST,
NOTIFY = BLE_GATT_CHR_F_NOTIFY,
INDICATE = BLE_GATT_CHR_F_INDICATE
} NIMBLE_PROPERTY;
# include "NimBLELocalAttribute.h"
# include "NimBLEAttValue.h"
# include <vector>
class NimBLEConnInfo;
class NimBLELocalValueAttribute : public NimBLELocalAttribute {
public:
/**
* @brief Get the properties of the attribute.
*/
uint16_t getProperties() const { return m_properties; }
/**
* @brief Get the length of the attribute value.
* @return The length of the attribute value.
*/
size_t getLength() const { return m_value.size(); }
/**
* @brief Get a copy of the value of the attribute value.
* @param [in] timestamp (Optional) A pointer to a time_t struct to get the time the value set.
* @return A copy of the attribute value.
*/
NimBLEAttValue getValue(time_t* timestamp = nullptr) const { return m_value; }
/**
* @brief Set the value of the attribute value.
* @param [in] data The data to set the value to.
* @param [in] size The size of the data.
*/
void setValue(const uint8_t* data, size_t size) { m_value.setValue(data, size); }
/**
* @brief Set the value of the attribute value.
* @param [in] str The string to set the value to.
*/
void setValue(const char* str) { m_value.setValue(str); }
/**
* @brief Set the value of the attribute value.
* @param [in] vec The vector to set the value to.
*/
void setValue(const std::vector<uint8_t>& vec) { m_value.setValue(vec); }
/**
* @brief Template to set the value to <type\>val.
* @param [in] val The value to set.
*/
template <typename T>
void setValue(const T& val) {
m_value.setValue<T>(val);
}
/**
* @brief Template to convert the data to <type\>.
* @tparam T The type to convert the data to.
* @param [in] timestamp (Optional) A pointer to a time_t struct to get the time the value set.
* @param [in] skipSizeCheck (Optional) If true it will skip checking if the data size is less than <tt>sizeof(<type\>)</tt>.
* @return The data converted to <type\> or NULL if skipSizeCheck is false and the data is less than <tt>sizeof(<type\>)</tt>.
* @details <b>Use:</b> <tt>getValue<type>(&timestamp, skipSizeCheck);</tt>
*/
template <typename T>
T getValue(time_t* timestamp = nullptr, bool skipSizeCheck = false) {
return m_value.getValue<T>(timestamp, skipSizeCheck);
}
protected:
friend class NimBLEServer;
/**
* @brief Construct a new NimBLELocalValueAttribute object.
* @param [in] uuid The UUID of the attribute.
* @param [in] handle The handle of the attribute.
* @param [in] maxLen The maximum length of the attribute value.
* @param [in] initLen The initial length of the attribute value.
*/
NimBLELocalValueAttribute(const NimBLEUUID& uuid,
uint16_t handle,
uint16_t maxLen,
uint16_t initLen = CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH)
: NimBLELocalAttribute(uuid, handle), m_value(initLen, maxLen) {}
/**
* @brief Destroy the NimBLELocalValueAttribute object.
*/
virtual ~NimBLELocalValueAttribute() = default;
/**
* @brief Callback function to support a read request.
* @param [in] connInfo A reference to a NimBLEConnInfo instance containing the peer info.
* @details This function is called by NimBLEServer when a read request is received.
*/
virtual void readEvent(const NimBLEConnInfo& connInfo) = 0;
/**
* @brief Callback function to support a write request.
* @param [in] val The value to write.
* @param [in] len The length of the value.
* @param [in] connInfo A reference to a NimBLEConnInfo instance containing the peer info.
* @details This function is called by NimBLEServer when a write request is received.
*/
virtual void writeEvent(const uint8_t* val, uint16_t len, const NimBLEConnInfo& connInfo) = 0;
/**
* @brief Get a pointer to value of the attribute.
* @return A pointer to the value of the attribute.
* @details This function is used by NimBLEServer when handling read/write requests.
*/
const NimBLEAttValue& getAttVal() const { return m_value; }
/**
* @brief Set the properties of the attribute.
* @param [in] properties The properties of the attribute.
*/
void setProperties(uint16_t properties) { m_properties = properties; }
NimBLEAttValue m_value{};
uint16_t m_properties{0};
};
#endif // CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL
#endif // _NIMBLE_LOCAL_VALUE_ATTRIBUTE_H_

View File

@@ -14,6 +14,7 @@
#if defined(CONFIG_NIMBLE_CPP_IDF) // using esp-idf
# include "esp_log.h"
# include "console/console.h"
# ifndef CONFIG_NIMBLE_CPP_LOG_LEVEL
# define CONFIG_NIMBLE_CPP_LOG_LEVEL 0
# endif
@@ -35,9 +36,6 @@
# define NIMBLE_LOGE(tag, format, ...) \
NIMBLE_CPP_LOG_PRINT(ESP_LOG_ERROR, tag, format, ##__VA_ARGS__)
# define NIMBLE_LOGC(tag, format, ...) \
NIMBLE_CPP_LOG_PRINT(ESP_LOG_ERROR, tag, format, ##__VA_ARGS__)
#else // using Arduino
# include "nimble/porting/nimble/include/syscfg/syscfg.h"
# include "nimble/console/console.h"
@@ -69,12 +67,13 @@
# if CONFIG_NIMBLE_CPP_LOG_LEVEL >= 1
# define NIMBLE_LOGE( tag, format, ... ) console_printf("E %s: " format "\n", tag, ##__VA_ARGS__)
# define NIMBLE_LOGC( tag, format, ... ) console_printf("CRIT %s: " format "\n", tag, ##__VA_ARGS__)
# else
# define NIMBLE_LOGE( tag, format, ... ) (void)tag
# define NIMBLE_LOGC( tag, format, ... ) (void)tag
# endif
#endif /* CONFIG_NIMBLE_CPP_IDF */
#define NIMBLE_LOGC( tag, format, ... ) console_printf("CRIT %s: " format "\n", tag, ##__VA_ARGS__)
#endif /* CONFIG_BT_ENABLED */
#endif /* MAIN_NIMBLELOG_H_ */

View File

@@ -15,17 +15,23 @@
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
#include "NimBLERemoteCharacteristic.h"
#include "NimBLEUtils.h"
#include "NimBLELog.h"
# include "NimBLERemoteCharacteristic.h"
# include "NimBLERemoteDescriptor.h"
# include "NimBLERemoteService.h"
# include "NimBLEClient.h"
# include "NimBLEUtils.h"
# include "NimBLELog.h"
#include <climits>
typedef struct {
const NimBLEUUID* uuid;
void* task_data;
} desc_filter_t;
static const char* LOG_TAG = "NimBLERemoteCharacteristic";
/**
* @brief Constructor.
* @param [in] reference to the service this characteristic belongs to.
* @param [in] svc A pointer to the service this characteristic belongs to.
* @param [in] ble_gatt_chr struct defined as:
* struct ble_gatt_chr {
* uint16_t def_handle;
@@ -34,34 +40,12 @@ static const char* LOG_TAG = "NimBLERemoteCharacteristic";
* ble_uuid_any_t uuid;
* };
*/
NimBLERemoteCharacteristic::NimBLERemoteCharacteristic(NimBLERemoteService *pRemoteService,
const struct ble_gatt_chr *chr)
{
NIMBLE_LOGD(LOG_TAG, ">> NimBLERemoteCharacteristic()");
switch (chr->uuid.u.type) {
case BLE_UUID_TYPE_16:
m_uuid = NimBLEUUID(chr->uuid.u16.value);
break;
case BLE_UUID_TYPE_32:
m_uuid = NimBLEUUID(chr->uuid.u32.value);
break;
case BLE_UUID_TYPE_128:
m_uuid = NimBLEUUID(const_cast<ble_uuid128_t*>(&chr->uuid.u128));
break;
default:
break;
}
m_handle = chr->val_handle;
m_defHandle = chr->def_handle;
m_endHandle = 0;
m_charProp = chr->properties;
m_pRemoteService = pRemoteService;
m_notifyCallback = nullptr;
NIMBLE_LOGD(LOG_TAG, "<< NimBLERemoteCharacteristic(): %s", m_uuid.toString().c_str());
} // NimBLERemoteCharacteristic
NimBLERemoteCharacteristic::NimBLERemoteCharacteristic(const NimBLERemoteService* svc, const ble_gatt_chr* chr)
: NimBLERemoteValueAttribute{chr->uuid, chr->val_handle},
m_pRemoteService{svc},
m_properties{chr->properties},
m_notifyCallback{},
m_descriptorVector{} {} // NimBLERemoteCharacteristic
/**
*@brief Destructor.
@@ -70,266 +54,115 @@ NimBLERemoteCharacteristic::~NimBLERemoteCharacteristic() {
deleteDescriptors();
} // ~NimBLERemoteCharacteristic
/*
#define BLE_GATT_CHR_PROP_BROADCAST 0x01
#define BLE_GATT_CHR_PROP_READ 0x02
#define BLE_GATT_CHR_PROP_WRITE_NO_RSP 0x04
#define BLE_GATT_CHR_PROP_WRITE 0x08
#define BLE_GATT_CHR_PROP_NOTIFY 0x10
#define BLE_GATT_CHR_PROP_INDICATE 0x20
#define BLE_GATT_CHR_PROP_AUTH_SIGN_WRITE 0x40
#define BLE_GATT_CHR_PROP_EXTENDED 0x80
*/
/**
* @brief Does the characteristic support broadcasting?
* @return True if the characteristic supports broadcasting.
*/
bool NimBLERemoteCharacteristic::canBroadcast() {
return (m_charProp & BLE_GATT_CHR_PROP_BROADCAST) != 0;
} // canBroadcast
/**
* @brief Does the characteristic support indications?
* @return True if the characteristic supports indications.
*/
bool NimBLERemoteCharacteristic::canIndicate() {
return (m_charProp & BLE_GATT_CHR_PROP_INDICATE) != 0;
} // canIndicate
/**
* @brief Does the characteristic support notifications?
* @return True if the characteristic supports notifications.
*/
bool NimBLERemoteCharacteristic::canNotify() {
return (m_charProp & BLE_GATT_CHR_PROP_NOTIFY) != 0;
} // canNotify
/**
* @brief Does the characteristic support reading?
* @return True if the characteristic supports reading.
*/
bool NimBLERemoteCharacteristic::canRead() {
return (m_charProp & BLE_GATT_CHR_PROP_READ) != 0;
} // canRead
/**
* @brief Does the characteristic support writing?
* @return True if the characteristic supports writing.
*/
bool NimBLERemoteCharacteristic::canWrite() {
return (m_charProp & BLE_GATT_CHR_PROP_WRITE) != 0;
} // canWrite
/**
* @brief Does the characteristic support writing with no response?
* @return True if the characteristic supports writing with no response.
*/
bool NimBLERemoteCharacteristic::canWriteNoResponse() {
return (m_charProp & BLE_GATT_CHR_PROP_WRITE_NO_RSP) != 0;
} // canWriteNoResponse
/**
* @brief Callback used by the API when a descriptor is discovered or search complete.
*/
int NimBLERemoteCharacteristic::descriptorDiscCB(uint16_t conn_handle,
const struct ble_gatt_error *error,
uint16_t chr_val_handle,
const struct ble_gatt_dsc *dsc,
void *arg)
{
int NimBLERemoteCharacteristic::descriptorDiscCB(
uint16_t conn_handle, const ble_gatt_error* error, uint16_t chr_val_handle, const ble_gatt_dsc* dsc, void* arg) {
int rc = error->status;
NIMBLE_LOGD(LOG_TAG, "Descriptor Discovered >> status: %d handle: %d",
rc, (rc == 0) ? dsc->handle : -1);
NIMBLE_LOGD(LOG_TAG, "Descriptor Discovery >> status: %d handle: %d", rc, (rc == 0) ? dsc->handle : -1);
desc_filter_t *filter = (desc_filter_t*)arg;
const NimBLEUUID *uuid_filter = filter->uuid;
ble_task_data_t *pTaskData = (ble_task_data_t*)filter->task_data;
NimBLERemoteCharacteristic *characteristic = (NimBLERemoteCharacteristic*)pTaskData->pATT;
auto filter = (desc_filter_t*)arg;
auto pTaskData = (ble_task_data_t*)filter->task_data;
const auto pChr = (NimBLERemoteCharacteristic*)pTaskData->pATT;
const NimBLEUUID* uuidFilter = filter->uuid;
if (characteristic->getRemoteService()->getClient()->getConnId() != conn_handle){
return 0;
}
switch (rc) {
case 0: {
if (uuid_filter != nullptr) {
if (ble_uuid_cmp(&uuid_filter->getNative()->u, &dsc->uuid.u) != 0) {
return 0;
} else {
rc = BLE_HS_EDONE;
}
}
NimBLERemoteDescriptor* pNewRemoteDescriptor = new NimBLERemoteDescriptor(characteristic, dsc);
characteristic->m_descriptorVector.push_back(pNewRemoteDescriptor);
break;
}
default:
break;
}
/* If rc == BLE_HS_EDONE, resume the task with a success error code and stop the discovery process.
* Else if rc == 0, just return 0 to continue the discovery until we get BLE_HS_EDONE.
* If we get any other error code tell the application to abort by returning non-zero in the rc.
*/
if (rc == BLE_HS_EDONE) {
pTaskData->rc = 0;
xTaskNotifyGive(pTaskData->task);
} else if(rc != 0) {
// Error; abort discovery.
pTaskData->rc = rc;
xTaskNotifyGive(pTaskData->task);
}
NIMBLE_LOGD(LOG_TAG,"<< Descriptor Discovered. status: %d", pTaskData->rc);
return rc;
}
/**
* @brief callback from NimBLE when the next characteristic of the service is discovered.
*/
int NimBLERemoteCharacteristic::nextCharCB(uint16_t conn_handle,
const struct ble_gatt_error *error,
const struct ble_gatt_chr *chr, void *arg)
{
int rc = error->status;
NIMBLE_LOGD(LOG_TAG, "Next Characteristic >> status: %d handle: %d",
rc, (rc == 0) ? chr->val_handle : -1);
ble_task_data_t *pTaskData = (ble_task_data_t*)arg;
NimBLERemoteCharacteristic *pChar = (NimBLERemoteCharacteristic*)pTaskData->pATT;
if (pChar->getRemoteService()->getClient()->getConnId() != conn_handle) {
return 0;
if (pChr->getHandle() != chr_val_handle) {
rc = BLE_HS_EDONE; // descriptor not for this characteristic
}
if (rc == 0) {
pChar->m_endHandle = chr->def_handle - 1;
rc = BLE_HS_EDONE;
} else if (rc == BLE_HS_EDONE) {
pChar->m_endHandle = pChar->getRemoteService()->getEndHandle();
} else {
pTaskData->rc = rc;
if (uuidFilter != nullptr) {
if (ble_uuid_cmp(uuidFilter->getBase(), &dsc->uuid.u) == 0) {
rc = BLE_HS_EDONE; // Found the descriptor, stop the search
} else {
return 0; // Not the descriptor we are looking for
}
}
pChr->m_descriptorVector.push_back(new NimBLERemoteDescriptor(pChr, dsc));
}
if (rc != 0) {
pTaskData->rc = rc == BLE_HS_EDONE ? 0 : rc;
NIMBLE_LOGD(LOG_TAG, "<< Descriptor Discovery");
xTaskNotifyGive(pTaskData->task);
}
xTaskNotifyGive(pTaskData->task);
return rc;
}
/**
* @brief Populate the descriptors (if any) for this characteristic.
* @param [in] the end handle of the characteristic, or the service, whichever comes first.
*/
bool NimBLERemoteCharacteristic::retrieveDescriptors(const NimBLEUUID *uuid_filter) {
bool NimBLERemoteCharacteristic::retrieveDescriptors(const NimBLEUUID* uuidFilter) const {
NIMBLE_LOGD(LOG_TAG, ">> retrieveDescriptors() for characteristic: %s", getUUID().toString().c_str());
// If this is the last handle then there are no descriptors
if (m_handle == getRemoteService()->getEndHandle()) {
return true;
}
TaskHandle_t cur_task = xTaskGetCurrentTaskHandle();
ble_task_data_t taskData = {const_cast<NimBLERemoteCharacteristic*>(this), cur_task, 0, nullptr};
desc_filter_t filter = {uuidFilter, &taskData};
int rc = 0;
TaskHandle_t cur_task = xTaskGetCurrentTaskHandle();
ble_task_data_t taskData = {this, cur_task, 0, nullptr};
// If we don't know the end handle of this characteristic retrieve the next one in the service
// The end handle is the next characteristic definition handle -1.
if (m_endHandle == 0) {
rc = ble_gattc_disc_all_chrs(getRemoteService()->getClient()->getConnId(),
m_handle,
int rc = ble_gattc_disc_all_dscs(getClient()->getConnId(),
getHandle(),
getRemoteService()->getEndHandle(),
NimBLERemoteCharacteristic::nextCharCB,
&taskData);
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "Error getting end handle rc=%d", rc);
return false;
}
#ifdef ulTaskNotifyValueClear
// Clear the task notification value to ensure we block
ulTaskNotifyValueClear(cur_task, ULONG_MAX);
#endif
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
if (taskData.rc != 0) {
NIMBLE_LOGE(LOG_TAG, "Could not retrieve end handle rc=%d", taskData.rc);
return false;
}
}
if (m_handle == m_endHandle) {
return true;
}
desc_filter_t filter = {uuid_filter, &taskData};
rc = ble_gattc_disc_all_dscs(getRemoteService()->getClient()->getConnId(),
m_handle,
m_endHandle,
NimBLERemoteCharacteristic::descriptorDiscCB,
&filter);
NimBLERemoteCharacteristic::descriptorDiscCB,
&filter);
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "ble_gattc_disc_all_dscs: rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc));
return false;
}
#ifdef ulTaskNotifyValueClear
# ifdef ulTaskNotifyValueClear
// Clear the task notification value to ensure we block
ulTaskNotifyValueClear(cur_task, ULONG_MAX);
#endif
# endif
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
if (taskData.rc != 0) {
NIMBLE_LOGE(LOG_TAG, "Failed to retrieve descriptors; startHandle:%d endHandle:%d taskData.rc=%d",
m_handle, m_endHandle, taskData.rc);
NIMBLE_LOGE(LOG_TAG,
"<< retrieveDescriptors(): failed: rc=%d %s",
taskData.rc,
NimBLEUtils::returnCodeToString(taskData.rc));
} else {
NIMBLE_LOGD(LOG_TAG, "<< retrieveDescriptors(): found %d descriptors.", m_descriptorVector.size());
}
NIMBLE_LOGD(LOG_TAG, "<< retrieveDescriptors(): Found %d descriptors.", m_descriptorVector.size());
return (taskData.rc == 0);
return taskData.rc == 0;
} // retrieveDescriptors
/**
* @brief Get the descriptor instance with the given UUID that belongs to this characteristic.
* @param [in] uuid The UUID of the descriptor to find.
* @return The Remote descriptor (if present) or null if not present.
* @return The Remote descriptor (if present) or nullptr if not present.
*/
NimBLERemoteDescriptor* NimBLERemoteCharacteristic::getDescriptor(const NimBLEUUID &uuid) {
NimBLERemoteDescriptor* NimBLERemoteCharacteristic::getDescriptor(const NimBLEUUID& uuid) const {
NIMBLE_LOGD(LOG_TAG, ">> getDescriptor: uuid: %s", uuid.toString().c_str());
NimBLERemoteDescriptor* pDsc = nullptr;
size_t prev_size = m_descriptorVector.size();
for(auto &it: m_descriptorVector) {
if(it->getUUID() == uuid) {
NIMBLE_LOGD(LOG_TAG, "<< getDescriptor: found the descriptor with uuid: %s", uuid.toString().c_str());
return it;
for (const auto& it : m_descriptorVector) {
if (it->getUUID() == uuid) {
pDsc = it;
goto Found;
}
}
size_t prev_size = m_descriptorVector.size();
if(retrieveDescriptors(&uuid)) {
if(m_descriptorVector.size() > prev_size) {
return m_descriptorVector.back();
if (retrieveDescriptors(&uuid)) {
if (m_descriptorVector.size() > prev_size) {
pDsc = m_descriptorVector.back();
goto Found;
}
// If the request was successful but 16/32 bit uuid not found
// try again with the 128 bit uuid.
if(uuid.bitSize() == BLE_UUID_TYPE_16 ||
uuid.bitSize() == BLE_UUID_TYPE_32)
{
if (uuid.bitSize() == BLE_UUID_TYPE_16 || uuid.bitSize() == BLE_UUID_TYPE_32) {
NimBLEUUID uuid128(uuid);
uuid128.to128();
if(retrieveDescriptors(&uuid128)) {
if(m_descriptorVector.size() > prev_size) {
return m_descriptorVector.back();
if (retrieveDescriptors(&uuid128)) {
if (m_descriptorVector.size() > prev_size) {
pDsc = m_descriptorVector.back();
goto Found;
}
}
} else {
@@ -339,9 +172,10 @@ NimBLERemoteDescriptor* NimBLERemoteCharacteristic::getDescriptor(const NimBLEUU
uuid16.to16();
// if the uuid was 128 bit but not of the BLE base type this check will fail
if (uuid16.bitSize() == BLE_UUID_TYPE_16) {
if(retrieveDescriptors(&uuid16)) {
if(m_descriptorVector.size() > prev_size) {
return m_descriptorVector.back();
if (retrieveDescriptors(&uuid16)) {
if (m_descriptorVector.size() > prev_size) {
pDsc = m_descriptorVector.back();
goto Found;
}
}
}
@@ -349,9 +183,13 @@ NimBLERemoteDescriptor* NimBLERemoteCharacteristic::getDescriptor(const NimBLEUU
}
NIMBLE_LOGD(LOG_TAG, "<< getDescriptor: Not found");
return nullptr;
} // getDescriptor
Found:
if (pDsc != nullptr) {
NIMBLE_LOGD(LOG_TAG, "<< getDescriptor: found");
}
return pDsc;
} // getDescriptor
/**
* @brief Get a pointer to the vector of found descriptors.
@@ -361,201 +199,39 @@ NimBLERemoteDescriptor* NimBLERemoteCharacteristic::getDescriptor(const NimBLEUU
* of this characteristic.
* @return A pointer to the vector of descriptors for this characteristic.
*/
std::vector<NimBLERemoteDescriptor*>* NimBLERemoteCharacteristic::getDescriptors(bool refresh) {
if(refresh) {
const std::vector<NimBLERemoteDescriptor*>& NimBLERemoteCharacteristic::getDescriptors(bool refresh) const {
if (refresh) {
deleteDescriptors();
if (!retrieveDescriptors()) {
NIMBLE_LOGE(LOG_TAG, "Error: Failed to get descriptors");
}
else{
NIMBLE_LOGI(LOG_TAG, "Found %d descriptor(s)", m_descriptorVector.size());
}
retrieveDescriptors();
}
return &m_descriptorVector;
} // getDescriptors
return m_descriptorVector;
} // getDescriptors
/**
* @brief Get iterator to the beginning of the vector of remote descriptor pointers.
* @return An iterator to the beginning of the vector of remote descriptor pointers.
*/
std::vector<NimBLERemoteDescriptor*>::iterator NimBLERemoteCharacteristic::begin() {
std::vector<NimBLERemoteDescriptor*>::iterator NimBLERemoteCharacteristic::begin() const {
return m_descriptorVector.begin();
}
/**
* @brief Get iterator to the end of the vector of remote descriptor pointers.
* @return An iterator to the end of the vector of remote descriptor pointers.
*/
std::vector<NimBLERemoteDescriptor*>::iterator NimBLERemoteCharacteristic::end() {
std::vector<NimBLERemoteDescriptor*>::iterator NimBLERemoteCharacteristic::end() const {
return m_descriptorVector.end();
}
/**
* @brief Get the handle for this characteristic.
* @return The handle for this characteristic.
*/
uint16_t NimBLERemoteCharacteristic::getHandle() {
return m_handle;
} // getHandle
/**
* @brief Get the handle for this characteristics definition.
* @return The handle for this characteristic definition.
*/
uint16_t NimBLERemoteCharacteristic::getDefHandle() {
return m_defHandle;
} // getDefHandle
/**
* @brief Get the remote service associated with this characteristic.
* @return The remote service associated with this characteristic.
*/
NimBLERemoteService* NimBLERemoteCharacteristic::getRemoteService() {
return m_pRemoteService;
NimBLERemoteService* NimBLERemoteCharacteristic::getRemoteService() const {
return const_cast<NimBLERemoteService*>(m_pRemoteService);
} // getRemoteService
/**
* @brief Get the UUID for this characteristic.
* @return The UUID for this characteristic.
*/
NimBLEUUID NimBLERemoteCharacteristic::getUUID() {
return m_uuid;
} // getUUID
/**
* @brief Get the value of the remote characteristic.
* @param [in] timestamp A pointer to a time_t struct to store the time the value was read.
* @return The value of the remote characteristic.
*/
NimBLEAttValue NimBLERemoteCharacteristic::getValue(time_t *timestamp) {
if(timestamp != nullptr) {
*timestamp = m_value.getTimeStamp();
}
return m_value;
} // getValue
/**
* @brief Read the value of the remote characteristic.
* @param [in] timestamp A pointer to a time_t struct to store the time the value was read.
* @return The value of the remote characteristic.
*/
NimBLEAttValue NimBLERemoteCharacteristic::readValue(time_t *timestamp) {
NIMBLE_LOGD(LOG_TAG, ">> readValue(): uuid: %s, handle: %d 0x%.2x",
getUUID().toString().c_str(), getHandle(), getHandle());
NimBLEClient* pClient = getRemoteService()->getClient();
NimBLEAttValue value;
if (!pClient->isConnected()) {
NIMBLE_LOGE(LOG_TAG, "Disconnected");
return value;
}
int rc = 0;
int retryCount = 1;
TaskHandle_t cur_task = xTaskGetCurrentTaskHandle();
ble_task_data_t taskData = {this, cur_task, 0, &value};
do {
rc = ble_gattc_read_long(pClient->getConnId(), m_handle, 0,
NimBLERemoteCharacteristic::onReadCB,
&taskData);
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "Error: Failed to read characteristic; rc=%d, %s",
rc, NimBLEUtils::returnCodeToString(rc));
return value;
}
#ifdef ulTaskNotifyValueClear
// Clear the task notification value to ensure we block
ulTaskNotifyValueClear(cur_task, ULONG_MAX);
#endif
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
rc = taskData.rc;
switch(rc){
case 0:
case BLE_HS_EDONE:
rc = 0;
break;
// Characteristic is not long-readable, return with what we have.
case BLE_HS_ATT_ERR(BLE_ATT_ERR_ATTR_NOT_LONG):
NIMBLE_LOGI(LOG_TAG, "Attribute not long");
rc = 0;
break;
case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_AUTHEN):
case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_AUTHOR):
case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_ENC):
if (retryCount && pClient->secureConnection())
break;
/* Else falls through. */
default:
NIMBLE_LOGE(LOG_TAG, "<< readValue rc=%d", rc);
return value;
}
} while(rc != 0 && retryCount--);
value.setTimeStamp();
m_value = value;
if(timestamp != nullptr) {
*timestamp = value.getTimeStamp();
}
NIMBLE_LOGD(LOG_TAG, "<< readValue length: %d rc=%d", value.length(), rc);
return value;
} // readValue
/**
* @brief Callback for characteristic read operation.
* @return success == 0 or error code.
*/
int NimBLERemoteCharacteristic::onReadCB(uint16_t conn_handle,
const struct ble_gatt_error *error,
struct ble_gatt_attr *attr, void *arg)
{
ble_task_data_t *pTaskData = (ble_task_data_t*)arg;
NimBLERemoteCharacteristic *characteristic = (NimBLERemoteCharacteristic*)pTaskData->pATT;
uint16_t conn_id = characteristic->getRemoteService()->getClient()->getConnId();
if(conn_id != conn_handle) {
return 0;
}
NIMBLE_LOGI(LOG_TAG, "Read complete; status=%d conn_handle=%d", error->status, conn_handle);
NimBLEAttValue *valBuf = (NimBLEAttValue*)pTaskData->buf;
int rc = error->status;
if(rc == 0) {
if(attr) {
uint16_t data_len = OS_MBUF_PKTLEN(attr->om);
if((valBuf->size() + data_len) > BLE_ATT_ATTR_MAX_LEN) {
rc = BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
} else {
NIMBLE_LOGD(LOG_TAG, "Got %u bytes", data_len);
valBuf->append(attr->om->om_data, data_len);
return 0;
}
}
}
pTaskData->rc = rc;
xTaskNotifyGive(pTaskData->task);
return rc;
} // onReadCB
/**
* @brief Subscribe or unsubscribe for notifications or indications.
* @param [in] val 0x00 to unsubscribe, 0x01 for notifications, 0x02 for indications.
@@ -564,23 +240,20 @@ int NimBLERemoteCharacteristic::onReadCB(uint16_t conn_handle,
* If NULL is provided then no callback is performed.
* @return false if writing to the descriptor failed.
*/
bool NimBLERemoteCharacteristic::setNotify(uint16_t val, notify_callback notifyCallback, bool response) {
NIMBLE_LOGD(LOG_TAG, ">> setNotify(): %s, %02x", toString().c_str(), val);
m_notifyCallback = notifyCallback;
bool NimBLERemoteCharacteristic::setNotify(uint16_t val, notify_callback notifyCallback, bool response) const {
NIMBLE_LOGD(LOG_TAG, ">> setNotify()");
m_notifyCallback = notifyCallback;
NimBLERemoteDescriptor* desc = getDescriptor(NimBLEUUID((uint16_t)0x2902));
if(desc == nullptr) {
if (desc == nullptr) {
NIMBLE_LOGW(LOG_TAG, "<< setNotify(): Callback set, CCCD not found");
return true;
}
NIMBLE_LOGD(LOG_TAG, "<< setNotify()");
return desc->writeValue((uint8_t *)&val, 2, response);
return desc->writeValue(reinterpret_cast<uint8_t*>(&val), 2, response);
} // setNotify
/**
* @brief Subscribe for notifications or indications.
* @param [in] notifications If true, subscribe for notifications, false subscribe for indications.
@@ -589,71 +262,127 @@ bool NimBLERemoteCharacteristic::setNotify(uint16_t val, notify_callback notifyC
* If NULL is provided then no callback is performed.
* @return false if writing to the descriptor failed.
*/
bool NimBLERemoteCharacteristic::subscribe(bool notifications, notify_callback notifyCallback, bool response) {
if(notifications) {
return setNotify(0x01, notifyCallback, response);
} else {
return setNotify(0x02, notifyCallback, response);
}
bool NimBLERemoteCharacteristic::subscribe(bool notifications, notify_callback notifyCallback, bool response) const {
return setNotify(notifications ? 0x01 : 0x02, notifyCallback, response);
} // subscribe
/**
* @brief Unsubscribe for notifications or indications.
* @param [in] response bool if true, require a write response from the descriptor write operation.
* @return false if writing to the descriptor failed.
*/
bool NimBLERemoteCharacteristic::unsubscribe(bool response) {
bool NimBLERemoteCharacteristic::unsubscribe(bool response) const {
return setNotify(0x00, nullptr, response);
} // unsubscribe
/**
* @brief Delete the descriptors in the descriptor vector.
* @details We maintain a vector called m_descriptorVector that contains pointers to NimBLERemoteDescriptors
* object references. Since we allocated these in this class, we are also responsible for deleting
* them. This method does just that.
*/
void NimBLERemoteCharacteristic::deleteDescriptors() {
void NimBLERemoteCharacteristic::deleteDescriptors() const {
NIMBLE_LOGD(LOG_TAG, ">> deleteDescriptors");
for(auto &it: m_descriptorVector) {
for (const auto& it : m_descriptorVector) {
delete it;
}
m_descriptorVector.clear();
std::vector<NimBLERemoteDescriptor*>().swap(m_descriptorVector);
NIMBLE_LOGD(LOG_TAG, "<< deleteDescriptors");
} // deleteDescriptors
/**
* @brief Delete descriptor by UUID
* @param [in] uuid The UUID of the descriptor to be deleted.
* @return Number of descriptors left in the vector.
*/
size_t NimBLERemoteCharacteristic::deleteDescriptor(const NimBLEUUID &uuid) {
size_t NimBLERemoteCharacteristic::deleteDescriptor(const NimBLEUUID& uuid) const {
NIMBLE_LOGD(LOG_TAG, ">> deleteDescriptor");
for(auto it = m_descriptorVector.begin(); it != m_descriptorVector.end(); ++it) {
if((*it)->getUUID() == uuid) {
delete *it;
for (auto it = m_descriptorVector.begin(); it != m_descriptorVector.end(); ++it) {
if ((*it)->getUUID() == uuid) {
delete (*it);
m_descriptorVector.erase(it);
break;
}
}
NIMBLE_LOGD(LOG_TAG, "<< deleteDescriptor");
return m_descriptorVector.size();
} // deleteDescriptor
/**
* @brief Does the characteristic support value broadcasting?
* @return True if supported.
*/
bool NimBLERemoteCharacteristic::canBroadcast() const {
return (m_properties & BLE_GATT_CHR_PROP_BROADCAST) != 0;
};
/**
* @brief Does the characteristic support reading?
* @return True if supported.
*/
bool NimBLERemoteCharacteristic::canRead() const {
return (m_properties & BLE_GATT_CHR_PROP_READ);
};
/**
* @brief Does the characteristic support writing without a response?
* @return True if supported.
*/
bool NimBLERemoteCharacteristic::canWriteNoResponse() const {
return (m_properties & BLE_GATT_CHR_PROP_WRITE_NO_RSP);
};
/**
* @brief Does the characteristic support writing?
* @return True if supported.
*/
bool NimBLERemoteCharacteristic::canWrite() const {
return (m_properties & BLE_GATT_CHR_PROP_WRITE);
};
/**
* @brief Does the characteristic support reading with encryption?
* @return True if supported.
*/
bool NimBLERemoteCharacteristic::canNotify() const {
return (m_properties & BLE_GATT_CHR_PROP_NOTIFY);
};
/**
* @brief Does the characteristic support indication?
* @return True if supported.
*/
bool NimBLERemoteCharacteristic::canIndicate() const {
return (m_properties & BLE_GATT_CHR_PROP_INDICATE);
};
/**
* @brief Does the characteristic support signed writing?
* @return True if supported.
*/
bool NimBLERemoteCharacteristic::canWriteSigned() const {
return (m_properties & BLE_GATT_CHR_PROP_AUTH_SIGN_WRITE);
};
/**
* @brief Does the characteristic support extended properties?
* @return True if supported.
*/
bool NimBLERemoteCharacteristic::hasExtendedProps() const {
return (m_properties & BLE_GATT_CHR_PROP_EXTENDED);
};
/**
* @brief Convert a NimBLERemoteCharacteristic to a string representation;
* @return a String representation.
*/
std::string NimBLERemoteCharacteristic::toString() {
std::string NimBLERemoteCharacteristic::toString() const {
std::string res = "Characteristic: uuid: " + m_uuid.toString();
char val[6];
char val[6];
res += ", handle: ";
snprintf(val, sizeof(val), "%d", getHandle());
res += val;
@@ -662,145 +391,18 @@ std::string NimBLERemoteCharacteristic::toString() {
res += val;
res += ", props: ";
res += " 0x";
snprintf(val, sizeof(val), "%02x", m_charProp);
snprintf(val, sizeof(val), "%02x", m_properties);
res += val;
for(auto &it: m_descriptorVector) {
for (const auto& it : m_descriptorVector) {
res += "\n" + it->toString();
}
return res;
} // toString
/**
* @brief Write a new value to the remote characteristic from a std::vector<uint8_t>.
* @param [in] vec A std::vector<uint8_t> value to write to the remote characteristic.
* @param [in] response Whether we require a response from the write.
* @return false if not connected or otherwise cannot perform write.
*/
bool NimBLERemoteCharacteristic::writeValue(const std::vector<uint8_t>& vec, bool response) {
return writeValue((uint8_t*)&vec[0], vec.size(), response);
} // writeValue
/**
* @brief Write a new value to the remote characteristic from a const char*.
* @param [in] char_s A character string to write to the remote characteristic.
* @param [in] response Whether we require a response from the write.
* @return false if not connected or otherwise cannot perform write.
*/
bool NimBLERemoteCharacteristic::writeValue(const char* char_s, bool response) {
return writeValue((uint8_t*)char_s, strlen(char_s), response);
} // writeValue
/**
* @brief Write a new value to the remote characteristic from a data buffer.
* @param [in] data A pointer to a data buffer.
* @param [in] length The length of the data in the data buffer.
* @param [in] response Whether we require a response from the write.
* @return false if not connected or otherwise cannot perform write.
*/
bool NimBLERemoteCharacteristic::writeValue(const uint8_t* data, size_t length, bool response) {
NIMBLE_LOGD(LOG_TAG, ">> writeValue(), length: %d", length);
NimBLEClient* pClient = getRemoteService()->getClient();
if (!pClient->isConnected()) {
NIMBLE_LOGE(LOG_TAG, "Disconnected");
return false;
}
int rc = 0;
int retryCount = 1;
uint16_t mtu = ble_att_mtu(pClient->getConnId()) - 3;
// Check if the data length is longer than we can write in one connection event.
// If so we must do a long write which requires a response.
if(length <= mtu && !response) {
rc = ble_gattc_write_no_rsp_flat(pClient->getConnId(), m_handle, data, length);
return (rc==0);
}
TaskHandle_t cur_task = xTaskGetCurrentTaskHandle();
ble_task_data_t taskData = {this, cur_task, 0, nullptr};
do {
if(length > mtu) {
NIMBLE_LOGI(LOG_TAG,"long write %d bytes", length);
os_mbuf *om = ble_hs_mbuf_from_flat(data, length);
rc = ble_gattc_write_long(pClient->getConnId(), m_handle, 0, om,
NimBLERemoteCharacteristic::onWriteCB,
&taskData);
} else {
rc = ble_gattc_write_flat(pClient->getConnId(), m_handle,
data, length,
NimBLERemoteCharacteristic::onWriteCB,
&taskData);
}
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "Error: Failed to write characteristic; rc=%d", rc);
return false;
}
#ifdef ulTaskNotifyValueClear
// Clear the task notification value to ensure we block
ulTaskNotifyValueClear(cur_task, ULONG_MAX);
#endif
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
rc = taskData.rc;
switch(rc){
case 0:
case BLE_HS_EDONE:
rc = 0;
break;
case BLE_HS_ATT_ERR(BLE_ATT_ERR_ATTR_NOT_LONG):
NIMBLE_LOGE(LOG_TAG, "Long write not supported by peer; Truncating length to %d", mtu);
retryCount++;
length = mtu;
break;
case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_AUTHEN):
case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_AUTHOR):
case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_ENC):
if (retryCount && pClient->secureConnection())
break;
/* Else falls through. */
default:
NIMBLE_LOGE(LOG_TAG, "<< writeValue, rc: %d", rc);
return false;
}
} while(rc != 0 && retryCount--);
NIMBLE_LOGD(LOG_TAG, "<< writeValue, rc: %d", rc);
return (rc == 0);
} // writeValue
/**
* @brief Callback for characteristic write operation.
* @return success == 0 or error code.
*/
int NimBLERemoteCharacteristic::onWriteCB(uint16_t conn_handle,
const struct ble_gatt_error *error,
struct ble_gatt_attr *attr, void *arg)
{
ble_task_data_t *pTaskData = (ble_task_data_t*)arg;
NimBLERemoteCharacteristic *characteristic = (NimBLERemoteCharacteristic*)pTaskData->pATT;
if(characteristic->getRemoteService()->getClient()->getConnId() != conn_handle){
return 0;
}
NIMBLE_LOGI(LOG_TAG, "Write complete; status=%d conn_handle=%d", error->status, conn_handle);
pTaskData->rc = error->status;
xTaskNotifyGive(pTaskData->task);
return 0;
}
const NimBLEClient* NimBLERemoteCharacteristic::getClient() const {
return getRemoteService()->getClient();
} // getClient
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL */

View File

@@ -12,169 +12,67 @@
* Author: kolban
*/
#ifndef COMPONENTS_NIMBLEREMOTECHARACTERISTIC_H_
#define COMPONENTS_NIMBLEREMOTECHARACTERISTIC_H_
#ifndef NIMBLE_CPP_REMOTE_CHARACTERISTIC_H_
#define NIMBLE_CPP_REMOTE_CHARACTERISTIC_H_
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
#include "NimBLERemoteService.h"
#include "NimBLERemoteDescriptor.h"
#include <vector>
#include <functional>
#include "NimBLELog.h"
# include "NimBLERemoteValueAttribute.h"
# include <vector>
# include <functional>
class NimBLERemoteService;
class NimBLERemoteDescriptor;
typedef std::function<void (NimBLERemoteCharacteristic* pBLERemoteCharacteristic,
uint8_t* pData, size_t length, bool isNotify)> notify_callback;
typedef struct {
const NimBLEUUID *uuid;
void *task_data;
} desc_filter_t;
/**
* @brief A model of a remote %BLE characteristic.
* @brief A model of a remote BLE characteristic.
*/
class NimBLERemoteCharacteristic {
public:
class NimBLERemoteCharacteristic : public NimBLERemoteValueAttribute {
public:
std::string toString() const;
NimBLERemoteService* getRemoteService() const;
void deleteDescriptors() const;
size_t deleteDescriptor(const NimBLEUUID& uuid) const;
bool canBroadcast() const;
bool canRead() const;
bool canWriteNoResponse() const;
bool canWrite() const;
bool canNotify() const;
bool canIndicate() const;
bool canWriteSigned() const;
bool hasExtendedProps() const;
const NimBLEClient* getClient() const override;
typedef std::function<void(NimBLERemoteCharacteristic* pBLERemoteCharacteristic, uint8_t* pData, size_t length, bool isNotify)> notify_callback;
bool subscribe(bool notifications = true, const notify_callback notifyCallback = nullptr, bool response = true) const;
bool unsubscribe(bool response = true) const;
std::vector<NimBLERemoteDescriptor*>::iterator begin() const;
std::vector<NimBLERemoteDescriptor*>::iterator end() const;
NimBLERemoteDescriptor* getDescriptor(const NimBLEUUID& uuid) const;
const std::vector<NimBLERemoteDescriptor*>& getDescriptors(bool refresh = false) const;
private:
friend class NimBLEClient;
friend class NimBLERemoteService;
NimBLERemoteCharacteristic(const NimBLERemoteService* pRemoteService, const ble_gatt_chr* chr);
~NimBLERemoteCharacteristic();
// Public member functions
bool canBroadcast();
bool canIndicate();
bool canNotify();
bool canRead();
bool canWrite();
bool canWriteNoResponse();
std::vector<NimBLERemoteDescriptor*>::iterator begin();
std::vector<NimBLERemoteDescriptor*>::iterator end();
NimBLERemoteDescriptor* getDescriptor(const NimBLEUUID &uuid);
std::vector<NimBLERemoteDescriptor*>* getDescriptors(bool refresh = false);
void deleteDescriptors();
size_t deleteDescriptor(const NimBLEUUID &uuid);
uint16_t getHandle();
uint16_t getDefHandle();
NimBLEUUID getUUID();
NimBLEAttValue readValue(time_t *timestamp = nullptr);
std::string toString();
NimBLERemoteService* getRemoteService();
NimBLEAttValue getValue(time_t *timestamp = nullptr);
bool subscribe(bool notifications = true,
notify_callback notifyCallback = nullptr,
bool response = true);
bool unsubscribe(bool response = true);
bool writeValue(const uint8_t* data,
size_t length,
bool response = false);
bool writeValue(const std::vector<uint8_t>& v, bool response = false);
bool writeValue(const char* s, bool response = false);
bool setNotify(uint16_t val, notify_callback notifyCallback = nullptr, bool response = true) const;
bool retrieveDescriptors(const NimBLEUUID* uuidFilter = nullptr) const;
static int descriptorDiscCB(
uint16_t conn_handle, const ble_gatt_error* error, uint16_t chr_val_handle, const ble_gatt_dsc* dsc, void* arg);
/*********************** Template Functions ************************/
const NimBLERemoteService* m_pRemoteService{nullptr};
uint8_t m_properties{0};
mutable notify_callback m_notifyCallback{nullptr};
mutable std::vector<NimBLERemoteDescriptor*> m_descriptorVector{};
/**
* @brief Template to set the remote characteristic value to <type\>val.
* @param [in] s The value to write.
* @param [in] response True == request write response.
* @details Only used for non-arrays and types without a `c_str()` method.
*/
template<typename T>
#ifdef _DOXYGEN_
bool
#else
typename std::enable_if<!std::is_array<T>::value && !Has_c_str_len<T>::value, bool>::type
#endif
writeValue(const T& s, bool response = false) {
return writeValue((uint8_t*)&s, sizeof(T), response);
}
/**
* @brief Template to set the remote characteristic value to <type\>val.
* @param [in] s The value to write.
* @param [in] response True == request write response.
* @details Only used if the <type\> has a `c_str()` method.
*/
template<typename T>
#ifdef _DOXYGEN_
bool
#else
typename std::enable_if<Has_c_str_len<T>::value, bool>::type
#endif
writeValue(const T& s, bool response = false) {
return writeValue((uint8_t*)s.c_str(), s.length(), response);
}
/**
* @brief Template to convert the remote characteristic data to <type\>.
* @tparam T The type to convert the data to.
* @param [in] timestamp A pointer to a time_t struct to store the time the value was read.
* @param [in] skipSizeCheck If true it will skip checking if the data size is less than <tt>sizeof(<type\>)</tt>.
* @return The data converted to <type\> or NULL if skipSizeCheck is false and the data is
* less than <tt>sizeof(<type\>)</tt>.
* @details <b>Use:</b> <tt>getValue<type>(&timestamp, skipSizeCheck);</tt>
*/
template<typename T>
T getValue(time_t *timestamp = nullptr, bool skipSizeCheck = false) {
if(!skipSizeCheck && m_value.size() < sizeof(T)) return T();
return *((T *)m_value.getValue(timestamp));
}
/**
* @brief Template to convert the remote characteristic data to <type\>.
* @tparam T The type to convert the data to.
* @param [in] timestamp A pointer to a time_t struct to store the time the value was read.
* @param [in] skipSizeCheck If true it will skip checking if the data size is less than <tt>sizeof(<type\>)</tt>.
* @return The data converted to <type\> or NULL if skipSizeCheck is false and the data is
* less than <tt>sizeof(<type\>)</tt>.
* @details <b>Use:</b> <tt>readValue<type>(&timestamp, skipSizeCheck);</tt>
*/
template<typename T>
T readValue(time_t *timestamp = nullptr, bool skipSizeCheck = false) {
NimBLEAttValue value = readValue();
if(!skipSizeCheck && value.size() < sizeof(T)) return T();
return *((T *)value.getValue(timestamp));
}
private:
NimBLERemoteCharacteristic(NimBLERemoteService *pRemoteservice, const struct ble_gatt_chr *chr);
friend class NimBLEClient;
friend class NimBLERemoteService;
friend class NimBLERemoteDescriptor;
// Private member functions
bool setNotify(uint16_t val, notify_callback notifyCallback = nullptr, bool response = true);
bool retrieveDescriptors(const NimBLEUUID *uuid_filter = nullptr);
static int onReadCB(uint16_t conn_handle, const struct ble_gatt_error *error,
struct ble_gatt_attr *attr, void *arg);
static int onWriteCB(uint16_t conn_handle, const struct ble_gatt_error *error,
struct ble_gatt_attr *attr, void *arg);
static int descriptorDiscCB(uint16_t conn_handle, const struct ble_gatt_error *error,
uint16_t chr_val_handle, const struct ble_gatt_dsc *dsc,
void *arg);
static int nextCharCB(uint16_t conn_handle, const struct ble_gatt_error *error,
const struct ble_gatt_chr *chr, void *arg);
// Private properties
NimBLEUUID m_uuid;
uint8_t m_charProp;
uint16_t m_handle;
uint16_t m_defHandle;
uint16_t m_endHandle;
NimBLERemoteService* m_pRemoteService;
NimBLEAttValue m_value;
notify_callback m_notifyCallback;
// We maintain a vector of descriptors owned by this characteristic.
std::vector<NimBLERemoteDescriptor*> m_descriptorVector;
}; // NimBLERemoteCharacteristic
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL */
#endif /* COMPONENTS_NIMBLEREMOTECHARACTERISTIC_H_ */
#endif /* NIMBLE_CPP_REMOTE_CHARACTERISTIC_H_ */

View File

@@ -15,183 +15,33 @@
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
#include "NimBLERemoteDescriptor.h"
#include "NimBLEUtils.h"
#include "NimBLELog.h"
#include <climits>
static const char* LOG_TAG = "NimBLERemoteDescriptor";
# include "NimBLERemoteDescriptor.h"
# include "NimBLERemoteCharacteristic.h"
/**
* @brief Remote descriptor constructor.
* @param [in] pRemoteCharacteristic A pointer to the Characteristic that this belongs to.
* @param [in] dsc A pointer to the struct that contains the descriptor information.
*/
NimBLERemoteDescriptor::NimBLERemoteDescriptor(NimBLERemoteCharacteristic* pRemoteCharacteristic,
const struct ble_gatt_dsc *dsc)
{
NIMBLE_LOGD(LOG_TAG, ">> NimBLERemoteDescriptor()");
switch (dsc->uuid.u.type) {
case BLE_UUID_TYPE_16:
m_uuid = NimBLEUUID(dsc->uuid.u16.value);
break;
case BLE_UUID_TYPE_32:
m_uuid = NimBLEUUID(dsc->uuid.u32.value);
break;
case BLE_UUID_TYPE_128:
m_uuid = NimBLEUUID(const_cast<ble_uuid128_t*>(&dsc->uuid.u128));
break;
default:
break;
}
m_handle = dsc->handle;
m_pRemoteCharacteristic = pRemoteCharacteristic;
NIMBLE_LOGD(LOG_TAG, "<< NimBLERemoteDescriptor(): %s", m_uuid.toString().c_str());
}
/**
* @brief Retrieve the handle associated with this remote descriptor.
* @return The handle associated with this remote descriptor.
*/
uint16_t NimBLERemoteDescriptor::getHandle() {
return m_handle;
} // getHandle
NimBLERemoteDescriptor::NimBLERemoteDescriptor(const NimBLERemoteCharacteristic* pRemoteCharacteristic,
const ble_gatt_dsc* dsc)
: NimBLERemoteValueAttribute{dsc->uuid, dsc->handle}, m_pRemoteCharacteristic{pRemoteCharacteristic} {} // NimBLERemoteDescriptor
/**
* @brief Get the characteristic that owns this descriptor.
* @return The characteristic that owns this descriptor.
*/
NimBLERemoteCharacteristic* NimBLERemoteDescriptor::getRemoteCharacteristic() {
return m_pRemoteCharacteristic;
NimBLERemoteCharacteristic* NimBLERemoteDescriptor::getRemoteCharacteristic() const {
return const_cast<NimBLERemoteCharacteristic*>(m_pRemoteCharacteristic);
} // getRemoteCharacteristic
/**
* @brief Retrieve the UUID associated this remote descriptor.
* @return The UUID associated this remote descriptor.
*/
NimBLEUUID NimBLERemoteDescriptor::getUUID() {
return m_uuid;
} // getUUID
/**
* @brief Read the value of the remote descriptor.
* @return The value of the remote descriptor.
*/
NimBLEAttValue NimBLERemoteDescriptor::readValue() {
NIMBLE_LOGD(LOG_TAG, ">> Descriptor readValue: %s", toString().c_str());
NimBLEClient* pClient = getRemoteCharacteristic()->getRemoteService()->getClient();
NimBLEAttValue value;
if (!pClient->isConnected()) {
NIMBLE_LOGE(LOG_TAG, "Disconnected");
return value;
}
int rc = 0;
int retryCount = 1;
TaskHandle_t cur_task = xTaskGetCurrentTaskHandle();
ble_task_data_t taskData = {this, cur_task, 0, &value};
do {
rc = ble_gattc_read_long(pClient->getConnId(), m_handle, 0,
NimBLERemoteDescriptor::onReadCB,
&taskData);
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "Error: Failed to read descriptor; rc=%d, %s",
rc, NimBLEUtils::returnCodeToString(rc));
return value;
}
#ifdef ulTaskNotifyValueClear
// Clear the task notification value to ensure we block
ulTaskNotifyValueClear(cur_task, ULONG_MAX);
#endif
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
rc = taskData.rc;
switch(rc){
case 0:
case BLE_HS_EDONE:
rc = 0;
break;
// Descriptor is not long-readable, return with what we have.
case BLE_HS_ATT_ERR(BLE_ATT_ERR_ATTR_NOT_LONG):
NIMBLE_LOGI(LOG_TAG, "Attribute not long");
rc = 0;
break;
case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_AUTHEN):
case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_AUTHOR):
case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_ENC):
if (retryCount && pClient->secureConnection())
break;
/* Else falls through. */
default:
return value;
}
} while(rc != 0 && retryCount--);
NIMBLE_LOGD(LOG_TAG, "<< Descriptor readValue(): length: %u rc=%d", value.length(), rc);
return value;
} // readValue
/**
* @brief Callback for Descriptor read operation.
* @return success == 0 or error code.
*/
int NimBLERemoteDescriptor::onReadCB(uint16_t conn_handle,
const struct ble_gatt_error *error,
struct ble_gatt_attr *attr, void *arg)
{
(void)attr;
ble_task_data_t *pTaskData = (ble_task_data_t*)arg;
NimBLERemoteDescriptor* desc = (NimBLERemoteDescriptor*)pTaskData->pATT;
uint16_t conn_id = desc->getRemoteCharacteristic()->getRemoteService()->getClient()->getConnId();
if(conn_id != conn_handle){
return 0;
}
NIMBLE_LOGD(LOG_TAG, "Read complete; status=%d conn_handle=%d", error->status, conn_handle);
NimBLEAttValue *valBuf = (NimBLEAttValue*)pTaskData->buf;
int rc = error->status;
if(rc == 0) {
if(attr) {
uint16_t data_len = OS_MBUF_PKTLEN(attr->om);
if((valBuf->size() + data_len) > BLE_ATT_ATTR_MAX_LEN) {
rc = BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
} else {
NIMBLE_LOGD(LOG_TAG, "Got %u bytes", data_len);
valBuf->append(attr->om->om_data, data_len);
return 0;
}
}
}
pTaskData->rc = rc;
xTaskNotifyGive(pTaskData->task);
return rc;
}
/**
* @brief Return a string representation of this Remote Descriptor.
* @return A string representation of this Remote Descriptor.
*/
std::string NimBLERemoteDescriptor::toString() {
std::string NimBLERemoteDescriptor::toString() const {
std::string res = "Descriptor: uuid: " + getUUID().toString();
char val[6];
char val[6];
res += ", handle: ";
snprintf(val, sizeof(val), "%d", getHandle());
res += val;
@@ -199,137 +49,8 @@ std::string NimBLERemoteDescriptor::toString() {
return res;
} // toString
/**
* @brief Callback for descriptor write operation.
* @return success == 0 or error code.
*/
int NimBLERemoteDescriptor::onWriteCB(uint16_t conn_handle,
const struct ble_gatt_error *error,
struct ble_gatt_attr *attr, void *arg)
{
ble_task_data_t *pTaskData = (ble_task_data_t*)arg;
NimBLERemoteDescriptor* descriptor = (NimBLERemoteDescriptor*)pTaskData->pATT;
if(descriptor->getRemoteCharacteristic()->getRemoteService()->getClient()->getConnId() != conn_handle){
return 0;
}
NIMBLE_LOGI(LOG_TAG, "Write complete; status=%d conn_handle=%d", error->status, conn_handle);
pTaskData->rc = error->status;
xTaskNotifyGive(pTaskData->task);
return 0;
const NimBLEClient* NimBLERemoteDescriptor::getClient() const {
return m_pRemoteCharacteristic->getClient();
}
/**
* @brief Write a new value to a remote descriptor from a std::vector<uint8_t>.
* @param [in] vec A std::vector<uint8_t> value to write to the remote descriptor.
* @param [in] response Whether we require a response from the write.
* @return false if not connected or otherwise cannot perform write.
*/
bool NimBLERemoteDescriptor::writeValue(const std::vector<uint8_t>& vec, bool response) {
return writeValue((uint8_t*)&vec[0], vec.size(), response);
} // writeValue
/**
* @brief Write a new value to the remote descriptor from a const char*.
* @param [in] char_s A character string to write to the remote descriptor.
* @param [in] response Whether we require a response from the write.
* @return false if not connected or otherwise cannot perform write.
*/
bool NimBLERemoteDescriptor::writeValue(const char* char_s, bool response) {
return writeValue((uint8_t*)char_s, strlen(char_s), response);
} // writeValue
/**
* @brief Write a new value to a remote descriptor.
* @param [in] data The data to send to the remote descriptor.
* @param [in] length The length of the data to send.
* @param [in] response True if we expect a write response.
* @return false if not connected or otherwise cannot perform write.
*/
bool NimBLERemoteDescriptor::writeValue(const uint8_t* data, size_t length, bool response) {
NIMBLE_LOGD(LOG_TAG, ">> Descriptor writeValue: %s", toString().c_str());
NimBLEClient* pClient = getRemoteCharacteristic()->getRemoteService()->getClient();
// Check to see that we are connected.
if (!pClient->isConnected()) {
NIMBLE_LOGE(LOG_TAG, "Disconnected");
return false;
}
int rc = 0;
int retryCount = 1;
uint16_t mtu = ble_att_mtu(pClient->getConnId()) - 3;
// Check if the data length is longer than we can write in 1 connection event.
// If so we must do a long write which requires a response.
if(length <= mtu && !response) {
rc = ble_gattc_write_no_rsp_flat(pClient->getConnId(), m_handle, data, length);
return (rc == 0);
}
TaskHandle_t cur_task = xTaskGetCurrentTaskHandle();
ble_task_data_t taskData = {this, cur_task, 0, nullptr};
do {
if(length > mtu) {
NIMBLE_LOGI(LOG_TAG,"long write %d bytes", length);
os_mbuf *om = ble_hs_mbuf_from_flat(data, length);
rc = ble_gattc_write_long(pClient->getConnId(), m_handle, 0, om,
NimBLERemoteDescriptor::onWriteCB,
&taskData);
} else {
rc = ble_gattc_write_flat(pClient->getConnId(), m_handle,
data, length,
NimBLERemoteDescriptor::onWriteCB,
&taskData);
}
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "Error: Failed to write descriptor; rc=%d", rc);
return false;
}
#ifdef ulTaskNotifyValueClear
// Clear the task notification value to ensure we block
ulTaskNotifyValueClear(cur_task, ULONG_MAX);
#endif
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
rc = taskData.rc;
switch(rc) {
case 0:
case BLE_HS_EDONE:
rc = 0;
break;
case BLE_HS_ATT_ERR(BLE_ATT_ERR_ATTR_NOT_LONG):
NIMBLE_LOGE(LOG_TAG, "Long write not supported by peer; Truncating length to %d", mtu);
retryCount++;
length = mtu;
break;
case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_AUTHEN):
case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_AUTHOR):
case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_ENC):
if (retryCount && pClient->secureConnection())
break;
/* Else falls through. */
default:
return false;
}
} while(rc != 0 && retryCount--);
NIMBLE_LOGD(LOG_TAG, "<< Descriptor writeValue, rc: %d",rc);
return (rc == 0);
} // writeValue
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL */

View File

@@ -12,93 +12,34 @@
* Author: kolban
*/
#ifndef COMPONENTS_NIMBLEREMOTEDESCRIPTOR_H_
#define COMPONENTS_NIMBLEREMOTEDESCRIPTOR_H_
#ifndef NIMBLE_CPP_REMOTE_DESCRIPTOR_H_
#define NIMBLE_CPP_REMOTE_DESCRIPTOR_H_
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
#include "NimBLERemoteCharacteristic.h"
# include "NimBLERemoteValueAttribute.h"
class NimBLERemoteCharacteristic;
class NimBLEClient;
/**
* @brief A model of remote %BLE descriptor.
* @brief A model of remote BLE descriptor.
*/
class NimBLERemoteDescriptor {
public:
uint16_t getHandle();
NimBLERemoteCharacteristic* getRemoteCharacteristic();
NimBLEUUID getUUID();
NimBLEAttValue readValue();
std::string toString(void);
bool writeValue(const uint8_t* data, size_t length, bool response = false);
bool writeValue(const std::vector<uint8_t>& v, bool response = false);
bool writeValue(const char* s, bool response = false);
class NimBLERemoteDescriptor : public NimBLERemoteValueAttribute {
public:
NimBLERemoteCharacteristic* getRemoteCharacteristic() const;
std::string toString(void) const;
const NimBLEClient* getClient() const override;
private:
friend class NimBLERemoteCharacteristic;
/*********************** Template Functions ************************/
NimBLERemoteDescriptor(const NimBLERemoteCharacteristic* pRemoteCharacteristic, const ble_gatt_dsc* dsc);
~NimBLERemoteDescriptor() = default;
/**
* @brief Template to set the remote descriptor value to <type\>val.
* @param [in] s The value to write.
* @param [in] response True == request write response.
* @details Only used for non-arrays and types without a `c_str()` method.
*/
template<typename T>
#ifdef _DOXYGEN_
bool
#else
typename std::enable_if<!std::is_array<T>::value && !Has_c_str_len<T>::value, bool>::type
#endif
writeValue(const T& s, bool response = false) {
return writeValue((uint8_t*)&s, sizeof(T), response);
}
/**
* @brief Template to set the remote descriptor value to <type\>val.
* @param [in] s The value to write.
* @param [in] response True == request write response.
* @details Only used if the <type\> has a `c_str()` method.
*/
template<typename T>
#ifdef _DOXYGEN_
bool
#else
typename std::enable_if<Has_c_str_len<T>::value, bool>::type
#endif
writeValue(const T& s, bool response = false) {
return writeValue((uint8_t*)s.c_str(), s.length(), response);
}
/**
* @brief Template to convert the remote descriptor data to <type\>.
* @tparam T The type to convert the data to.
* @param [in] skipSizeCheck If true it will skip checking if the data size is less than <tt>sizeof(<type\>)</tt>.
* @return The data converted to <type\> or NULL if skipSizeCheck is false and the data is
* less than <tt>sizeof(<type\>)</tt>.
* @details <b>Use:</b> <tt>readValue<type>(skipSizeCheck);</tt>
*/
template<typename T>
T readValue(bool skipSizeCheck = false) {
NimBLEAttValue value = readValue();
if(!skipSizeCheck && value.size() < sizeof(T)) return T();
return *((T *)value.data());
}
private:
friend class NimBLERemoteCharacteristic;
NimBLERemoteDescriptor (NimBLERemoteCharacteristic* pRemoteCharacteristic,
const struct ble_gatt_dsc *dsc);
static int onWriteCB(uint16_t conn_handle, const struct ble_gatt_error *error,
struct ble_gatt_attr *attr, void *arg);
static int onReadCB(uint16_t conn_handle, const struct ble_gatt_error *error,
struct ble_gatt_attr *attr, void *arg);
uint16_t m_handle;
NimBLEUUID m_uuid;
NimBLERemoteCharacteristic* m_pRemoteCharacteristic;
const NimBLERemoteCharacteristic* m_pRemoteCharacteristic;
};
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL */
#endif /* COMPONENTS_NIMBLEREMOTEDESCRIPTOR_H_ */
#endif /* NIMBLE_CPP_REMOTE_DESCRIPTOR_H_ */

View File

@@ -15,12 +15,12 @@
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
#include "NimBLERemoteService.h"
#include "NimBLEUtils.h"
#include "NimBLEDevice.h"
#include "NimBLELog.h"
#include <climits>
# include "NimBLERemoteService.h"
# include "NimBLERemoteCharacteristic.h"
# include "NimBLEClient.h"
# include "NimBLEAttValue.h"
# include "NimBLEUtils.h"
# include "NimBLELog.h"
static const char* LOG_TAG = "NimBLERemoteService";
@@ -29,28 +29,8 @@ static const char* LOG_TAG = "NimBLERemoteService";
* @param [in] pClient A pointer to the client this belongs to.
* @param [in] service A pointer to the structure with the service information.
*/
NimBLERemoteService::NimBLERemoteService(NimBLEClient* pClient, const struct ble_gatt_svc* service) {
NIMBLE_LOGD(LOG_TAG, ">> NimBLERemoteService()");
m_pClient = pClient;
switch (service->uuid.u.type) {
case BLE_UUID_TYPE_16:
m_uuid = NimBLEUUID(service->uuid.u16.value);
break;
case BLE_UUID_TYPE_32:
m_uuid = NimBLEUUID(service->uuid.u32.value);
break;
case BLE_UUID_TYPE_128:
m_uuid = NimBLEUUID(const_cast<ble_uuid128_t*>(&service->uuid.u128));
break;
default:
break;
}
m_startHandle = service->start_handle;
m_endHandle = service->end_handle;
NIMBLE_LOGD(LOG_TAG, "<< NimBLERemoteService(): %s", m_uuid.toString().c_str());
}
NimBLERemoteService::NimBLERemoteService(const NimBLEClient* pClient, const ble_gatt_svc* service)
: NimBLEAttribute{service->uuid, service->start_handle}, m_pClient{pClient}, m_endHandle{service->end_handle} {}
/**
* @brief When deleting the service make sure we delete all characteristics and descriptors.
@@ -59,66 +39,63 @@ NimBLERemoteService::~NimBLERemoteService() {
deleteCharacteristics();
}
/**
* @brief Get iterator to the beginning of the vector of remote characteristic pointers.
* @return An iterator to the beginning of the vector of remote characteristic pointers.
*/
std::vector<NimBLERemoteCharacteristic*>::iterator NimBLERemoteService::begin() {
return m_characteristicVector.begin();
std::vector<NimBLERemoteCharacteristic*>::iterator NimBLERemoteService::begin() const {
return m_vChars.begin();
}
/**
* @brief Get iterator to the end of the vector of remote characteristic pointers.
* @return An iterator to the end of the vector of remote characteristic pointers.
*/
std::vector<NimBLERemoteCharacteristic*>::iterator NimBLERemoteService::end() {
return m_characteristicVector.end();
std::vector<NimBLERemoteCharacteristic*>::iterator NimBLERemoteService::end() const {
return m_vChars.end();
}
/**
* @brief Get the remote characteristic object for the characteristic UUID.
* @param [in] uuid Remote characteristic uuid.
* @return A pointer to the remote characteristic object.
*/
NimBLERemoteCharacteristic* NimBLERemoteService::getCharacteristic(const char* uuid) {
NimBLERemoteCharacteristic* NimBLERemoteService::getCharacteristic(const char* uuid) const {
return getCharacteristic(NimBLEUUID(uuid));
} // getCharacteristic
/**
* @brief Get the characteristic object for the UUID.
* @param [in] uuid Characteristic uuid.
* @return A pointer to the characteristic object, or nullptr if not found.
*/
NimBLERemoteCharacteristic* NimBLERemoteService::getCharacteristic(const NimBLEUUID &uuid) {
NimBLERemoteCharacteristic* NimBLERemoteService::getCharacteristic(const NimBLEUUID& uuid) const {
NIMBLE_LOGD(LOG_TAG, ">> getCharacteristic: uuid: %s", uuid.toString().c_str());
NimBLERemoteCharacteristic* pChar = nullptr;
size_t prev_size = m_vChars.size();
for(auto &it: m_characteristicVector) {
if(it->getUUID() == uuid) {
NIMBLE_LOGD(LOG_TAG, "<< getCharacteristic: found the characteristic with uuid: %s", uuid.toString().c_str());
return it;
for (const auto& it : m_vChars) {
if (it->getUUID() == uuid) {
pChar = it;
goto Found;
}
}
size_t prev_size = m_characteristicVector.size();
if(retrieveCharacteristics(&uuid)) {
if(m_characteristicVector.size() > prev_size) {
return m_characteristicVector.back();
if (retrieveCharacteristics(&uuid)) {
if (m_vChars.size() > prev_size) {
pChar = m_vChars.back();
goto Found;
}
// If the request was successful but 16/32 bit uuid not found
// try again with the 128 bit uuid.
if(uuid.bitSize() == BLE_UUID_TYPE_16 ||
uuid.bitSize() == BLE_UUID_TYPE_32)
{
if (uuid.bitSize() == BLE_UUID_TYPE_16 || uuid.bitSize() == BLE_UUID_TYPE_32) {
NimBLEUUID uuid128(uuid);
uuid128.to128();
if (retrieveCharacteristics(&uuid128)) {
if(m_characteristicVector.size() > prev_size) {
return m_characteristicVector.back();
if (m_vChars.size() > prev_size) {
pChar = m_vChars.back();
goto Found;
}
}
} else {
@@ -128,283 +105,193 @@ NimBLERemoteCharacteristic* NimBLERemoteService::getCharacteristic(const NimBLEU
uuid16.to16();
// if the uuid was 128 bit but not of the BLE base type this check will fail
if (uuid16.bitSize() == BLE_UUID_TYPE_16) {
if(retrieveCharacteristics(&uuid16)) {
if(m_characteristicVector.size() > prev_size) {
return m_characteristicVector.back();
if (retrieveCharacteristics(&uuid16)) {
if (m_vChars.size() > prev_size) {
pChar = m_vChars.back();
goto Found;
}
}
}
}
}
NIMBLE_LOGD(LOG_TAG, "<< getCharacteristic: not found");
NIMBLE_LOGD(LOG_TAG, "<< Characteristic not found");
return nullptr;
} // getCharacteristic
Found:
NIMBLE_LOGD(LOG_TAG, "<< Found characteristic");
return pChar;
} // getCharacteristic
/**
* @brief Get a pointer to the vector of found characteristics.
* @param [in] refresh If true the current characteristics vector will cleared and
* all characteristics for this service retrieved from the peripheral.
* If false the vector will be returned with the currently stored characteristics of this service.
* @return A pointer to the vector of descriptors for this characteristic.
* @return A read-only reference to the vector of characteristics retrieved for this service.
*/
std::vector<NimBLERemoteCharacteristic*>* NimBLERemoteService::getCharacteristics(bool refresh) {
if(refresh) {
const std::vector<NimBLERemoteCharacteristic*>& NimBLERemoteService::getCharacteristics(bool refresh) const {
if (refresh) {
deleteCharacteristics();
if (!retrieveCharacteristics()) {
NIMBLE_LOGE(LOG_TAG, "Error: Failed to get characteristics");
}
else{
NIMBLE_LOGI(LOG_TAG, "Found %d characteristics", m_characteristicVector.size());
}
retrieveCharacteristics();
}
return &m_characteristicVector;
} // getCharacteristics
return m_vChars;
} // getCharacteristics
/**
* @brief Callback for Characteristic discovery.
* @return success == 0 or error code.
*/
int NimBLERemoteService::characteristicDiscCB(uint16_t conn_handle,
const struct ble_gatt_error *error,
const struct ble_gatt_chr *chr, void *arg)
{
NIMBLE_LOGD(LOG_TAG,"Characteristic Discovered >> status: %d handle: %d",
error->status, (error->status == 0) ? chr->val_handle : -1);
ble_task_data_t *pTaskData = (ble_task_data_t*)arg;
NimBLERemoteService *service = (NimBLERemoteService*)pTaskData->pATT;
int NimBLERemoteService::characteristicDiscCB(uint16_t conn_handle,
const ble_gatt_error* error,
const ble_gatt_chr* chr,
void* arg) {
NIMBLE_LOGD(LOG_TAG, "Characteristic Discovery >>");
auto pTaskData = (ble_task_data_t*)arg;
const auto pSvc = (NimBLERemoteService*)pTaskData->pATT;
// Make sure the discovery is for this device
if(service->getClient()->getConnId() != conn_handle){
if (pSvc->getClient()->getConnId() != conn_handle) {
return 0;
}
if(error->status == 0) {
// Found a service - add it to the vector
NimBLERemoteCharacteristic* pRemoteCharacteristic = new NimBLERemoteCharacteristic(service, chr);
service->m_characteristicVector.push_back(pRemoteCharacteristic);
return 0;
}
if(error->status == BLE_HS_EDONE) {
pTaskData->rc = 0;
if (error->status == 0) {
pSvc->m_vChars.push_back(new NimBLERemoteCharacteristic(pSvc, chr));
} else {
NIMBLE_LOGE(LOG_TAG, "characteristicDiscCB() rc=%d %s",
error->status,
NimBLEUtils::returnCodeToString(error->status));
pTaskData->rc = error->status;
pTaskData->rc = error->status == BLE_HS_EDONE ? 0 : error->status;
NIMBLE_LOGD(LOG_TAG, "<< Characteristic Discovery");
xTaskNotifyGive(pTaskData->task);
}
xTaskNotifyGive(pTaskData->task);
NIMBLE_LOGD(LOG_TAG,"<< Characteristic Discovered");
return error->status;
}
/**
* @brief Retrieve all the characteristics for this service.
* This function will not return until we have all the characteristics.
* @return True if successful.
*/
bool NimBLERemoteService::retrieveCharacteristics(const NimBLEUUID *uuid_filter) {
NIMBLE_LOGD(LOG_TAG, ">> retrieveCharacteristics() for service: %s", getUUID().toString().c_str());
bool NimBLERemoteService::retrieveCharacteristics(const NimBLEUUID* uuidFilter) const {
NIMBLE_LOGD(LOG_TAG, ">> retrieveCharacteristics()");
int rc = 0;
TaskHandle_t cur_task = xTaskGetCurrentTaskHandle();
ble_task_data_t taskData = {const_cast<NimBLERemoteService*>(this), cur_task, 0, nullptr};
int rc = 0;
TaskHandle_t cur_task = xTaskGetCurrentTaskHandle();
ble_task_data_t taskData = {this, cur_task, 0, nullptr};
if(uuid_filter == nullptr) {
if (uuidFilter == nullptr) {
rc = ble_gattc_disc_all_chrs(m_pClient->getConnId(),
m_startHandle,
m_endHandle,
NimBLERemoteService::characteristicDiscCB,
&taskData);
getHandle(),
getEndHandle(),
NimBLERemoteService::characteristicDiscCB,
&taskData);
} else {
rc = ble_gattc_disc_chrs_by_uuid(m_pClient->getConnId(),
m_startHandle,
m_endHandle,
&uuid_filter->getNative()->u,
NimBLERemoteService::characteristicDiscCB,
&taskData);
getHandle(),
getEndHandle(),
uuidFilter->getBase(),
NimBLERemoteService::characteristicDiscCB,
&taskData);
}
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "ble_gattc_disc_all_chrs: rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc));
return false;
if (rc == 0) {
# ifdef ulTaskNotifyValueClear
// Clear the task notification value to ensure we block
ulTaskNotifyValueClear(cur_task, ULONG_MAX);
# endif
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
}
#ifdef ulTaskNotifyValueClear
// Clear the task notification value to ensure we block
ulTaskNotifyValueClear(cur_task, ULONG_MAX);
#endif
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
if(taskData.rc == 0){
if (uuid_filter == nullptr) {
if (m_characteristicVector.size() > 1) {
for (auto it = m_characteristicVector.begin(); it != m_characteristicVector.end(); ++it ) {
auto nx = std::next(it, 1);
if (nx == m_characteristicVector.end()) {
break;
}
(*it)->m_endHandle = (*nx)->m_defHandle - 1;
}
}
if (m_characteristicVector.size() > 0) {
m_characteristicVector.back()->m_endHandle = getEndHandle();
}
}
if (taskData.rc != 0) {
NIMBLE_LOGE(LOG_TAG, "<< retrieveCharacteristics() rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc));
} else {
NIMBLE_LOGD(LOG_TAG, "<< retrieveCharacteristics()");
return true;
}
NIMBLE_LOGE(LOG_TAG, "Could not retrieve characteristics");
return false;
return taskData.rc == 0;
} // retrieveCharacteristics
/**
* @brief Get the client associated with this service.
* @return A reference to the client associated with this service.
*/
NimBLEClient* NimBLERemoteService::getClient() {
const NimBLEClient* NimBLERemoteService::getClient() const {
return m_pClient;
} // getClient
/**
* @brief Get the service end handle.
*/
uint16_t NimBLERemoteService::getEndHandle() {
return m_endHandle;
} // getEndHandle
/**
* @brief Get the service start handle.
*/
uint16_t NimBLERemoteService::getStartHandle() {
return m_startHandle;
} // getStartHandle
/**
* @brief Get the service UUID.
*/
NimBLEUUID NimBLERemoteService::getUUID() {
return m_uuid;
}
/**
* @brief Read the value of a characteristic associated with this service.
* @param [in] characteristicUuid The characteristic to read.
* @param [in] uuid The characteristic to read.
* @returns a string containing the value or an empty string if not found or error.
*/
std::string NimBLERemoteService::getValue(const NimBLEUUID &characteristicUuid) {
NIMBLE_LOGD(LOG_TAG, ">> readValue: uuid: %s", characteristicUuid.toString().c_str());
std::string ret = "";
NimBLERemoteCharacteristic* pChar = getCharacteristic(characteristicUuid);
if(pChar != nullptr) {
ret = pChar->readValue();
NimBLEAttValue NimBLERemoteService::getValue(const NimBLEUUID& uuid) const {
const auto pChar = getCharacteristic(uuid);
if (pChar) {
return pChar->readValue();
}
NIMBLE_LOGD(LOG_TAG, "<< readValue");
return ret;
return NimBLEAttValue{};
} // readValue
/**
* @brief Set the value of a characteristic.
* @param [in] characteristicUuid The characteristic to set.
* @param [in] uuid The characteristic UUID to set.
* @param [in] value The value to set.
* @returns true on success, false if not found or error
*/
bool NimBLERemoteService::setValue(const NimBLEUUID &characteristicUuid, const std::string &value) {
NIMBLE_LOGD(LOG_TAG, ">> setValue: uuid: %s", characteristicUuid.toString().c_str());
bool ret = false;
NimBLERemoteCharacteristic* pChar = getCharacteristic(characteristicUuid);
if(pChar != nullptr) {
ret = pChar->writeValue(value);
bool NimBLERemoteService::setValue(const NimBLEUUID& uuid, const NimBLEAttValue& value) const {
const auto pChar = getCharacteristic(uuid);
if (pChar) {
return pChar->writeValue(value);
}
NIMBLE_LOGD(LOG_TAG, "<< setValue");
return ret;
return false;
} // setValue
/**
* @brief Delete the characteristics in the characteristics vector.
* @details We maintain a vector called m_characteristicsVector that contains pointers to BLERemoteCharacteristic
* object references. Since we allocated these in this class, we are also responsible for deleting
* them. This method does just that.
*/
void NimBLERemoteService::deleteCharacteristics() {
NIMBLE_LOGD(LOG_TAG, ">> deleteCharacteristics");
for(auto &it: m_characteristicVector) {
void NimBLERemoteService::deleteCharacteristics() const {
for (const auto& it : m_vChars) {
delete it;
}
m_characteristicVector.clear();
NIMBLE_LOGD(LOG_TAG, "<< deleteCharacteristics");
std::vector<NimBLERemoteCharacteristic*>{}.swap(m_vChars);
} // deleteCharacteristics
/**
* @brief Delete characteristic by UUID
* @param [in] uuid The UUID of the characteristic to be removed from the local database.
* @return Number of characteristics left.
*/
size_t NimBLERemoteService::deleteCharacteristic(const NimBLEUUID &uuid) {
NIMBLE_LOGD(LOG_TAG, ">> deleteCharacteristic");
for(auto it = m_characteristicVector.begin(); it != m_characteristicVector.end(); ++it) {
if((*it)->getUUID() == uuid) {
delete *it;
m_characteristicVector.erase(it);
size_t NimBLERemoteService::deleteCharacteristic(const NimBLEUUID& uuid) const {
for (auto it = m_vChars.begin(); it != m_vChars.end(); ++it) {
if ((*it)->getUUID() == uuid) {
delete (*it);
m_vChars.erase(it);
break;
}
}
NIMBLE_LOGD(LOG_TAG, "<< deleteCharacteristic");
return m_characteristicVector.size();
return m_vChars.size();
} // deleteCharacteristic
/**
* @brief Create a string representation of this remote service.
* @return A string representation of this remote service.
*/
std::string NimBLERemoteService::toString() {
std::string res = "Service: uuid: " + m_uuid.toString();
char val[6];
res += ", start_handle: ";
snprintf(val, sizeof(val), "%d", m_startHandle);
std::string NimBLERemoteService::toString() const {
std::string res = "Service: uuid: " + m_uuid.toString() + ", start_handle: 0x";
char val[5];
snprintf(val, sizeof(val), "%04x", getHandle());
res += val;
snprintf(val, sizeof(val), "%04x", m_startHandle);
res += " 0x";
res += val;
res += ", end_handle: ";
snprintf(val, sizeof(val), "%d", m_endHandle);
res += val;
snprintf(val, sizeof(val), "%04x", m_endHandle);
res += " 0x";
res += ", end_handle: 0x";
snprintf(val, sizeof(val), "%04x", getEndHandle());
res += val;
for (auto &it: m_characteristicVector) {
res += "\n" + it->toString();
for (const auto& chr : m_vChars) {
res += "\n" + chr->toString();
}
return res;

View File

@@ -12,74 +12,54 @@
* Author: kolban
*/
#ifndef COMPONENTS_NIMBLEREMOTESERVICE_H_
#define COMPONENTS_NIMBLEREMOTESERVICE_H_
#ifndef NIMBLE_CPP_REMOTE_SERVICE_H_
#define NIMBLE_CPP_REMOTE_SERVICE_H_
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
#include "NimBLEClient.h"
#include "NimBLEUUID.h"
#include "NimBLERemoteCharacteristic.h"
# include "NimBLEAttribute.h"
# include <vector>
#include <vector>
class NimBLEClient;
class NimBLERemoteCharacteristic;
class NimBLEClient;
class NimBLEAttValue;
/**
* @brief A model of a remote %BLE service.
* @brief A model of a remote BLE service.
*/
class NimBLERemoteService {
public:
virtual ~NimBLERemoteService();
class NimBLERemoteService : public NimBLEAttribute {
public:
NimBLERemoteCharacteristic* getCharacteristic(const char* uuid) const;
NimBLERemoteCharacteristic* getCharacteristic(const NimBLEUUID& uuid) const;
void deleteCharacteristics() const;
size_t deleteCharacteristic(const NimBLEUUID& uuid) const;
const NimBLEClient* getClient(void) const;
NimBLEAttValue getValue(const NimBLEUUID& characteristicUuid) const;
bool setValue(const NimBLEUUID& characteristicUuid, const NimBLEAttValue& value) const;
std::string toString(void) const;
uint16_t getStartHandle() const { return getHandle(); }
uint16_t getEndHandle() const { return m_endHandle; }
// Public methods
std::vector<NimBLERemoteCharacteristic*>::iterator begin();
std::vector<NimBLERemoteCharacteristic*>::iterator end();
NimBLERemoteCharacteristic* getCharacteristic(const char* uuid);
NimBLERemoteCharacteristic* getCharacteristic(const NimBLEUUID &uuid);
void deleteCharacteristics();
size_t deleteCharacteristic(const NimBLEUUID &uuid);
NimBLEClient* getClient(void);
//uint16_t getHandle();
NimBLEUUID getUUID(void);
std::string getValue(const NimBLEUUID &characteristicUuid);
bool setValue(const NimBLEUUID &characteristicUuid,
const std::string &value);
std::string toString(void);
std::vector<NimBLERemoteCharacteristic*>* getCharacteristics(bool refresh = false);
const std::vector<NimBLERemoteCharacteristic*>& getCharacteristics(bool refresh = false) const;
std::vector<NimBLERemoteCharacteristic*>::iterator begin() const;
std::vector<NimBLERemoteCharacteristic*>::iterator end() const;
private:
// Private constructor ... never meant to be created by a user application.
NimBLERemoteService(NimBLEClient* pClient, const struct ble_gatt_svc *service);
// Friends
private:
friend class NimBLEClient;
friend class NimBLERemoteCharacteristic;
// Private methods
bool retrieveCharacteristics(const NimBLEUUID *uuid_filter = nullptr);
static int characteristicDiscCB(uint16_t conn_handle,
const struct ble_gatt_error *error,
const struct ble_gatt_chr *chr,
void *arg);
NimBLERemoteService(const NimBLEClient* pClient, const struct ble_gatt_svc* service);
~NimBLERemoteService();
bool retrieveCharacteristics(const NimBLEUUID* uuidFilter = nullptr) const;
static int characteristicDiscCB(uint16_t conn_handle,
const struct ble_gatt_error* error,
const struct ble_gatt_chr* chr,
void* arg);
uint16_t getStartHandle();
uint16_t getEndHandle();
void releaseSemaphores();
// Properties
// We maintain a vector of characteristics owned by this service.
std::vector<NimBLERemoteCharacteristic*> m_characteristicVector;
NimBLEClient* m_pClient;
NimBLEUUID m_uuid;
uint16_t m_startHandle;
uint16_t m_endHandle;
mutable std::vector<NimBLERemoteCharacteristic*> m_vChars{};
const NimBLEClient* m_pClient{nullptr};
const uint16_t m_endHandle{0};
}; // NimBLERemoteService
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL */
#endif /* COMPONENTS_NIMBLEREMOTESERVICE_H_ */
#endif /* NIMBLE_CPP_REMOTE_SERVICE_H_*/

View File

@@ -0,0 +1,210 @@
/*
* NimBLERemoteValueAttribute.cpp
*
* Created: on July 28 2024
* Author H2zero
*/
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
# include "NimBLERemoteValueAttribute.h"
# include "NimBLEClient.h"
const char* LOG_TAG = "NimBLERemoteValueAttribute";
bool NimBLERemoteValueAttribute::writeValue(const uint8_t* data, size_t length, bool response) const {
NIMBLE_LOGD(LOG_TAG, ">> writeValue()");
const NimBLEClient* pClient = getClient();
TaskHandle_t cur_task = xTaskGetCurrentTaskHandle();
ble_task_data_t taskData = {const_cast<NimBLERemoteValueAttribute*>(this), cur_task, 0, nullptr};
int retryCount = 1;
int rc = 0;
uint16_t mtu = pClient->getMTU() - 3;
// Check if the data length is longer than we can write in one connection event.
// If so we must do a long write which requires a response.
if (length <= mtu && !response) {
rc = ble_gattc_write_no_rsp_flat(pClient->getConnId(), getHandle(), data, length);
goto Done;
}
do {
if (length > mtu) {
NIMBLE_LOGI(LOG_TAG, "writeValue: long write");
os_mbuf* om = ble_hs_mbuf_from_flat(data, length);
rc = ble_gattc_write_long(pClient->getConnId(), getHandle(), 0, om, NimBLERemoteValueAttribute::onWriteCB, &taskData);
} else {
rc = ble_gattc_write_flat(pClient->getConnId(),
getHandle(),
data,
length,
NimBLERemoteValueAttribute::onWriteCB,
&taskData);
}
if (rc != 0) {
goto Done;
}
# ifdef ulTaskNotifyValueClear
// Clear the task notification value to ensure we block
ulTaskNotifyValueClear(cur_task, ULONG_MAX);
# endif
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
rc = taskData.rc;
switch (rc) {
case 0:
case BLE_HS_EDONE:
rc = 0;
break;
case BLE_HS_ATT_ERR(BLE_ATT_ERR_ATTR_NOT_LONG):
NIMBLE_LOGE(LOG_TAG, "Long write not supported by peer; Truncating length to %d", mtu);
retryCount++;
length = mtu;
break;
case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_AUTHEN):
case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_AUTHOR):
case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_ENC):
if (retryCount && pClient->secureConnection()) break;
/* Else falls through. */
default:
goto Done;
}
} while (rc != 0 && retryCount--);
Done:
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "<< writeValue failed, rc: %d %s", rc, NimBLEUtils::returnCodeToString(rc));
} else {
NIMBLE_LOGD(LOG_TAG, "<< writeValue, rc: %d", rc);
}
return (rc == 0);
} // writeValue
/**
* @brief Callback for characteristic write operation.
* @return success == 0 or error code.
*/
int NimBLERemoteValueAttribute::onWriteCB(uint16_t conn_handle, const ble_gatt_error* error, ble_gatt_attr* attr, void* arg) {
auto pTaskData = static_cast<ble_task_data_t*>(arg);
const auto pAtt = static_cast<NimBLERemoteValueAttribute*>(pTaskData->pATT);
if (pAtt->getClient()->getConnId() != conn_handle) {
return 0;
}
NIMBLE_LOGI(LOG_TAG, "Write complete; status=%d", error->status);
pTaskData->rc = error->status;
xTaskNotifyGive(pTaskData->task);
return 0;
}
/**
* @brief Read the value of the remote characteristic.
* @param [in] timestamp A pointer to a time_t struct to store the time the value was read.
* @return The value of the remote characteristic.
*/
NimBLEAttValue NimBLERemoteValueAttribute::readValue(time_t* timestamp) const {
NIMBLE_LOGD(LOG_TAG, ">> readValue()");
NimBLEAttValue value{};
const NimBLEClient* pClient = getClient();
int rc = 0;
int retryCount = 1;
TaskHandle_t cur_task = xTaskGetCurrentTaskHandle();
ble_task_data_t taskData = {const_cast<NimBLERemoteValueAttribute*>(this), cur_task, 0, &value};
do {
rc = ble_gattc_read_long(pClient->getConnId(), getHandle(), 0, NimBLERemoteValueAttribute::onReadCB, &taskData);
if (rc != 0) {
goto Done;
}
# ifdef ulTaskNotifyValueClear
// Clear the task notification value to ensure we block
ulTaskNotifyValueClear(cur_task, ULONG_MAX);
# endif
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
rc = taskData.rc;
switch (rc) {
case 0:
case BLE_HS_EDONE:
rc = 0;
break;
// Characteristic is not long-readable, return with what we have.
case BLE_HS_ATT_ERR(BLE_ATT_ERR_ATTR_NOT_LONG):
NIMBLE_LOGI(LOG_TAG, "Attribute not long");
rc = ble_gattc_read(pClient->getConnId(), getHandle(), NimBLERemoteValueAttribute::onReadCB, &taskData);
if (rc != 0) {
goto Done;
}
retryCount++;
break;
case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_AUTHEN):
case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_AUTHOR):
case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_ENC):
if (retryCount && pClient->secureConnection()) break;
/* Else falls through. */
default:
goto Done;
}
} while (rc != 0 && retryCount--);
value.setTimeStamp();
m_value = value;
if (timestamp != nullptr) {
*timestamp = value.getTimeStamp();
}
Done:
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "<< readValue failed rc=%d, %s", rc, NimBLEUtils::returnCodeToString(rc));
} else {
NIMBLE_LOGD(LOG_TAG, "<< readValue rc=%d", rc);
}
return value;
} // readValue
/**
* @brief Callback for characteristic read operation.
* @return success == 0 or error code.
*/
int NimBLERemoteValueAttribute::onReadCB(uint16_t conn_handle, const ble_gatt_error* error, ble_gatt_attr* attr, void* arg) {
auto pTaskData = static_cast<ble_task_data_t*>(arg);
const auto pAtt = static_cast<NimBLERemoteValueAttribute*>(pTaskData->pATT);
if (pAtt->getClient()->getConnId() != conn_handle) {
return 0;
}
int rc = error->status;
NIMBLE_LOGI(LOG_TAG, "Read complete; status=%d", rc);
if (rc == 0) {
if (attr) {
auto valBuf = static_cast<NimBLEAttValue*>(pTaskData->buf);
uint16_t data_len = OS_MBUF_PKTLEN(attr->om);
if ((valBuf->size() + data_len) > BLE_ATT_ATTR_MAX_LEN) {
rc = BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
} else {
NIMBLE_LOGD(LOG_TAG, "Got %u bytes", data_len);
valBuf->append(attr->om->om_data, data_len);
return 0;
}
}
}
pTaskData->rc = rc;
xTaskNotifyGive(pTaskData->task);
return rc;
} // onReadCB
#endif // CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL

View File

@@ -0,0 +1,161 @@
/*
* NimBLERemoteValueAttribute.h
*
* Created: on July 28 2024
* Author H2zero
*/
#ifndef _NIMBLE_CPP_REMOTE_VALUE_ATTRIBUTE_H_
#define _NIMBLE_CPP_REMOTE_VALUE_ATTRIBUTE_H_
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
# if defined(CONFIG_NIMBLE_CPP_IDF)
# include <host/ble_gatt.h>
# else
# include <nimble/nimble/host/include/host/ble_gatt.h>
# endif
# include "NimBLEAttribute.h"
# include "NimBLEAttValue.h"
class NimBLEClient;
class NimBLERemoteValueAttribute : public NimBLEAttribute {
public:
/**
* @brief Read the value of the remote attribute.
* @param [in] timestamp A pointer to a time_t struct to store the time the value was read.
* @return The value of the remote attribute.
*/
NimBLEAttValue readValue(time_t* timestamp = nullptr) const;
/**
* @brief Get the length of the remote attribute value.
* @return The length of the remote attribute value.
*/
size_t getLength() const { return m_value.size(); }
/**
* @brief Get the value of the remote attribute.
* @return The value of the remote attribute.
* @details This returns a copy of the value to avoid potential race conditions.
*/
NimBLEAttValue getValue() const { return m_value; }
/**
* Get the client instance that owns this attribute.
*/
virtual const NimBLEClient* getClient() const = 0;
/**
* @brief Write a new value to the remote characteristic from a data buffer.
* @param [in] data A pointer to a data buffer.
* @param [in] length The length of the data in the data buffer.
* @param [in] response Whether we require a response from the write.
* @return false if not connected or otherwise cannot perform write.
*/
bool writeValue(const uint8_t* data, size_t length, bool response = false) const;
/**
* @brief Write a new value to the remote characteristic from a std::vector<uint8_t>.
* @param [in] vec A std::vector<uint8_t> value to write to the remote characteristic.
* @param [in] response Whether we require a response from the write.
* @return false if not connected or otherwise cannot perform write.
*/
bool writeValue(const std::vector<uint8_t>& v, bool response = false) const {
return writeValue(&v[0], v.size(), response);
}
/**
* @brief Write a new value to the remote characteristic from a const char*.
* @param [in] str A character string to write to the remote characteristic.
* @param [in] length (optional) The length of the character string, uses strlen if omitted.
* @param [in] response Whether we require a response from the write.
* @return false if not connected or otherwise cannot perform write.
*/
bool writeValue(const char* str, size_t length = 0, bool response = false) const {
return writeValue(reinterpret_cast<const uint8_t*>(str), length ? length : strlen(str), response);
}
/**
* @brief Template to set the remote characteristic value to <type\>val.
* @param [in] s The value to write.
* @param [in] response True == request write response.
* @details Only used for non-arrays and types without a `c_str()` method.
*/
template <typename T>
# ifdef _DOXYGEN_
bool
# else
typename std::enable_if<!std::is_array<T>::value && !Has_c_str_len<T>::value, bool>::type
# endif
writeValue(const T& v, bool response = false) const {
return writeValue(reinterpret_cast<const uint8_t*>(&v), sizeof(T), response);
}
/**
* @brief Template to set the remote characteristic value to <type\>val.
* @param [in] s The value to write.
* @param [in] response True == request write response.
* @details Only used if the <type\> has a `c_str()` method.
*/
template <typename T>
# ifdef _DOXYGEN_
bool
# else
typename std::enable_if<Has_c_str_len<T>::value, bool>::type
# endif
writeValue(const T& s, bool response = false) const {
return writeValue(reinterpret_cast<const uint8_t*>(s.c_str()), s.length(), response);
}
/**
* @brief Template to convert the remote characteristic data to <type\>.
* @tparam T The type to convert the data to.
* @param [in] timestamp A pointer to a time_t struct to store the time the value was read.
* @param [in] skipSizeCheck If true it will skip checking if the data size is less than <tt>sizeof(<type\>)</tt>.
* @return The data converted to <type\> or NULL if skipSizeCheck is false and the data is
* less than <tt>sizeof(<type\>)</tt>.
* @details <b>Use:</b> <tt>getValue<type>(&timestamp, skipSizeCheck);</tt>
*/
template <typename T>
T getValue(time_t* timestamp = nullptr, bool skipSizeCheck = false) const {
return m_value.getValue<T>(timestamp, skipSizeCheck);
}
/**
* @brief Template to convert the remote characteristic data to <type\>.
* @tparam T The type to convert the data to.
* @param [in] timestamp A pointer to a time_t struct to store the time the value was read.
* @param [in] skipSizeCheck If true it will skip checking if the data size is less than <tt>sizeof(<type\>)</tt>.
* @return The data converted to <type\> or NULL if skipSizeCheck is false and the data is
* less than <tt>sizeof(<type\>)</tt>.
* @details <b>Use:</b> <tt>readValue<type>(&timestamp, skipSizeCheck);</tt>
*/
template <typename T>
T readValue(time_t* timestamp = nullptr, bool skipSizeCheck = false) const {
readValue();
return m_value.getValue<T>(timestamp, skipSizeCheck);
}
protected:
/**
* @brief Construct a new NimBLERemoteValueAttribute object.
*/
NimBLERemoteValueAttribute(const ble_uuid_any_t& uuid, uint16_t handle) : NimBLEAttribute(uuid, handle) {}
/**
* @brief Destroy the NimBLERemoteValueAttribute object.
*/
virtual ~NimBLERemoteValueAttribute() = default;
static int onReadCB(uint16_t conn_handle, const ble_gatt_error* error, ble_gatt_attr* attr, void* arg);
static int onWriteCB(uint16_t conn_handle, const ble_gatt_error* error, ble_gatt_attr* attr, void* arg);
mutable NimBLEAttValue m_value{};
};
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL */
#endif // _NIMBLE_CPP_REMOTE_VALUE_ATTRIBUTE_H_

View File

@@ -111,29 +111,17 @@ int NimBLEScan::handleGapEvent(ble_gap_event* event, void* arg) {
return 0;
}
advertisedDevice = new NimBLEAdvertisedDevice();
advertisedDevice->setAddress(advertisedAddress);
advertisedDevice->setAdvType(event_type, isLegacyAdv);
#if CONFIG_BT_NIMBLE_EXT_ADV
advertisedDevice->setSetId(disc.sid);
advertisedDevice->setPrimaryPhy(disc.prim_phy);
advertisedDevice->setSecondaryPhy(disc.sec_phy);
advertisedDevice->setPeriodicInterval(disc.periodic_adv_itvl);
#endif
advertisedDevice = new NimBLEAdvertisedDevice(event, event_type);
pScan->m_scanResults.m_advertisedDevicesVector.push_back(advertisedDevice);
NIMBLE_LOGI(LOG_TAG, "New advertiser: %s", advertisedAddress.toString().c_str());
} else if (advertisedDevice != nullptr) {
advertisedDevice->update(event, event_type);
NIMBLE_LOGI(LOG_TAG, "Updated advertiser: %s", advertisedAddress.toString().c_str());
} else {
// Scan response from unknown device
return 0;
}
advertisedDevice->m_timestamp = time(nullptr);
advertisedDevice->setRSSI(disc.rssi);
advertisedDevice->setPayload(disc.data, disc.length_data, (isLegacyAdv &&
event_type == BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP));
if (pScan->m_pScanCallbacks) {
if (advertisedDevice->m_callbackSent == 0 || !pScan->m_scan_params.filter_duplicates) {
advertisedDevice->m_callbackSent = 1;
@@ -359,7 +347,7 @@ bool NimBLEScan::start(uint32_t duration, bool is_continue) {
case BLE_HS_EOS:
case BLE_HS_ECONTROLLER:
case BLE_HS_ENOTSYNCED:
NIMBLE_LOGC(LOG_TAG, "Unable to scan - Host Reset");
NIMBLE_LOGE(LOG_TAG, "Unable to scan - Host Reset");
break;
default:

View File

@@ -27,6 +27,12 @@
#include "nimble/nimble/host/services/gatt/include/services/gatt/ble_svc_gatt.h"
#endif
#include <limits.h>
#include <algorithm>
#define NIMBLE_SERVER_GET_PEER_NAME_ON_CONNECT_CB 0
#define NIMBLE_SERVER_GET_PEER_NAME_ON_AUTH_CB 1
static const char* LOG_TAG = "NimBLEServer";
static NimBLEServerCallbacks defaultCallbacks;
@@ -47,6 +53,7 @@ NimBLEServer::NimBLEServer() {
#endif
m_svcChanged = false;
m_deleteCallbacks = true;
m_getPeerNameOnConnect = false;
} // NimBLEServer
@@ -186,9 +193,8 @@ void NimBLEServer::start() {
int rc = ble_gatts_start();
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "ble_gatts_start; rc=%d, %s", rc,
NimBLEUtils::returnCodeToString(rc));
abort();
NIMBLE_LOGE(LOG_TAG, "ble_gatts_start; rc=%d, %s", rc, NimBLEUtils::returnCodeToString(rc));
return;
}
#if CONFIG_NIMBLE_CPP_LOG_LEVEL >= 4
@@ -212,8 +218,8 @@ void NimBLEServer::start() {
// Get the assigned service handles and build a vector of characteristics
// with Notify / Indicate capabilities for event handling
for(auto &svc : m_svcVec) {
if(svc->m_removed == 0) {
rc = ble_gatts_find_svc(&svc->getUUID().getNative()->u, &svc->m_handle);
if(svc->getRemoved() == 0) {
rc = ble_gatts_find_svc(svc->getUUID().getBase(), &svc->m_handle);
if(rc != 0) {
NIMBLE_LOGW(LOG_TAG, "GATT Server started without service: %s, Service %s",
svc->getUUID().toString().c_str(), svc->isStarted() ? "missing" : "not started");
@@ -228,6 +234,13 @@ void NimBLEServer::start() {
(chr->m_properties & BLE_GATT_CHR_F_NOTIFY)) {
m_notifyChrVec.push_back(chr);
}
for (auto &desc : chr->m_dscVec) {
ble_gatts_find_dsc(svc->getUUID().getBase(),
chr->getUUID().getBase(),
desc->getUUID().getBase(),
&desc->m_handle);
}
}
}
@@ -274,6 +287,14 @@ void NimBLEServer::advertiseOnDisconnect(bool aod) {
} // advertiseOnDisconnect
#endif
/**
* @brief Set the server to automatically read the name from the connected peer before
* the onConnect callback is called and enables the override callback with name parameter.
* @param [in] enable Enable reading the connected peer name upon connection.
*/
void NimBLEServer::getPeerNameOnConnect(bool enable) {
m_getPeerNameOnConnect = enable;
} // getPeerNameOnConnect
/**
* @brief Return the number of connected clients.
@@ -311,12 +332,8 @@ NimBLEConnInfo NimBLEServer::getPeerInfo(size_t index) {
* @param [in] address The address of the peer.
*/
NimBLEConnInfo NimBLEServer::getPeerInfo(const NimBLEAddress& address) {
ble_addr_t peerAddr;
memcpy(&peerAddr.val, address.getNative(),6);
peerAddr.type = address.getType();
NimBLEConnInfo peerInfo;
int rc = ble_gap_conn_find_by_addr(&peerAddr, &peerInfo.m_desc);
int rc = ble_gap_conn_find_by_addr(address.getBase(), &peerInfo.m_desc);
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "Peer info not found");
}
@@ -340,6 +357,113 @@ NimBLEConnInfo NimBLEServer::getPeerIDInfo(uint16_t id) {
return peerInfo;
} // getPeerIDInfo
/**
* @brief Callback that is called after reading from the peer name characteristic.
* @details This will check the task pointer in the task data struct to determine
* the action to take once the name has been read. If there is a task waiting then
* it will be woken, if not, the the RC value is checked to determine which callback
* should be called.
*/
int NimBLEServer::peerNameCB(uint16_t conn_handle,
const struct ble_gatt_error *error,
struct ble_gatt_attr *attr,
void *arg) {
ble_task_data_t *pTaskData = (ble_task_data_t*)arg;
std::string *name = (std::string*)pTaskData->buf;
int rc = error->status;
if (rc == 0) {
if (attr) {
name->append(OS_MBUF_DATA(attr->om, char*), OS_MBUF_PKTLEN(attr->om));
return rc;
}
}
if (rc == BLE_HS_EDONE) {
// No ask means this was read for a callback.
if (pTaskData->task == nullptr) {
NimBLEServer* pServer = (NimBLEServer*)pTaskData->pATT;
NimBLEConnInfo peerInfo{};
ble_gap_conn_find(conn_handle, &peerInfo.m_desc);
// Use the rc value as a flag to indicate which callback should be called.
if (pTaskData->rc == NIMBLE_SERVER_GET_PEER_NAME_ON_CONNECT_CB) {
pServer->m_pServerCallbacks->onConnect(pServer, peerInfo, *name);
} else if (pTaskData->rc == NIMBLE_SERVER_GET_PEER_NAME_ON_AUTH_CB) {
pServer->m_pServerCallbacks->onAuthenticationComplete(peerInfo, *name);
}
}
} else {
NIMBLE_LOGE(LOG_TAG, "NimBLEServerPeerNameCB rc=%d; %s", rc, NimBLEUtils::returnCodeToString(rc));
}
if (pTaskData->task != nullptr) {
pTaskData->rc = rc;
xTaskNotifyGive(pTaskData->task);
} else {
// If the read was triggered for callback use then these were allocated.
delete name;
delete pTaskData;
}
return rc;
}
/**
* @brief Internal method that sends the read command.
*/
std::string NimBLEServer::getPeerNameInternal(uint16_t conn_handle, TaskHandle_t task, int cb_type) {
std::string *buf = new std::string{};
ble_task_data_t *taskData = new ble_task_data_t{this, task, cb_type, buf};
ble_uuid16_t uuid {{BLE_UUID_TYPE_16}, BLE_SVC_GAP_CHR_UUID16_DEVICE_NAME};
int rc = ble_gattc_read_by_uuid(conn_handle,
1,
0xffff,
&uuid.u,
NimBLEServer::peerNameCB,
taskData);
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "ble_gattc_read_by_uuid rc=%d, %s", rc, NimBLEUtils::returnCodeToString(rc));
NimBLEConnInfo peerInfo{};
ble_gap_conn_find(conn_handle, &peerInfo.m_desc);
if (cb_type == NIMBLE_SERVER_GET_PEER_NAME_ON_CONNECT_CB) {
m_pServerCallbacks->onConnect(this, peerInfo, *buf);
} else if (cb_type == NIMBLE_SERVER_GET_PEER_NAME_ON_AUTH_CB) {
m_pServerCallbacks->onAuthenticationComplete(peerInfo, *buf);
}
delete buf;
delete taskData;
} else if (task != nullptr) {
#ifdef ulTaskNotifyValueClear
// Clear the task notification value to ensure we block
ulTaskNotifyValueClear(task, ULONG_MAX);
#endif
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
rc = taskData->rc;
std::string name{*(std::string*)taskData->buf};
delete buf;
delete taskData;
if (rc != 0 && rc != BLE_HS_EDONE) {
NIMBLE_LOGE(LOG_TAG, "getPeerName rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc));
}
return name;
}
// TaskData and name buffer will be deleted in the callback.
return "";
}
/**
* @brief Get the name of the connected peer.
* @param connInfo A reference to a NimBLEConnInfo instance to read the name from.
* @returns A string containing the name.
* @note This is a blocking call and should NOT be called from any callbacks!
*/
std::string NimBLEServer::getPeerName(const NimBLEConnInfo& connInfo) {
std::string name = getPeerNameInternal(connInfo.getConnHandle(), xTaskGetCurrentTaskHandle());
return name;
}
/**
* @brief Handle a GATT Server Event.
@@ -366,16 +490,22 @@ int NimBLEServer::handleGapEvent(struct ble_gap_event *event, void *arg) {
#if !CONFIG_BT_NIMBLE_EXT_ADV
NimBLEDevice::startAdvertising();
#endif
}
else {
pServer->m_connectedPeersVec.push_back(event->connect.conn_handle);
} else {
rc = ble_gap_conn_find(event->connect.conn_handle, &peerInfo.m_desc);
if (rc != 0) {
return 0;
}
pServer->m_pServerCallbacks->onConnect(pServer, peerInfo);
pServer->m_connectedPeersVec.push_back(event->connect.conn_handle);
if (pServer->m_getPeerNameOnConnect) {
pServer->getPeerNameInternal(event->connect.conn_handle,
nullptr,
NIMBLE_SERVER_GET_PEER_NAME_ON_CONNECT_CB);
} else {
pServer->m_pServerCallbacks->onConnect(pServer, peerInfo);
}
}
return 0;
@@ -390,7 +520,7 @@ int NimBLEServer::handleGapEvent(struct ble_gap_event *event, void *arg) {
case BLE_HS_EOS:
case BLE_HS_ECONTROLLER:
case BLE_HS_ENOTSYNCED:
NIMBLE_LOGC(LOG_TAG, "Disconnect - host reset, rc=%d", event->disconnect.reason);
NIMBLE_LOGE(LOG_TAG, "Disconnect - host reset, rc=%d", event->disconnect.reason);
NimBLEDevice::onReset(event->disconnect.reason);
break;
default:
@@ -438,7 +568,7 @@ int NimBLEServer::handleGapEvent(struct ble_gap_event *event, void *arg) {
}
}
it->setSubscribe(event);
it->setSubscribe(event, peerInfo);
break;
}
}
@@ -526,7 +656,13 @@ int NimBLEServer::handleGapEvent(struct ble_gap_event *event, void *arg) {
return BLE_ATT_ERR_INVALID_HANDLE;
}
pServer->m_pServerCallbacks->onAuthenticationComplete(peerInfo);
if (pServer->m_getPeerNameOnConnect) {
pServer->getPeerNameInternal(event->enc_change.conn_handle,
nullptr,
NIMBLE_SERVER_GET_PEER_NAME_ON_AUTH_CB);
} else {
pServer->m_pServerCallbacks->onAuthenticationComplete(peerInfo);
}
return 0;
} // BLE_GAP_EVENT_ENC_CHANGE
@@ -591,6 +727,68 @@ int NimBLEServer::handleGapEvent(struct ble_gap_event *event, void *arg) {
} // handleGapEvent
/**
* @brief STATIC callback to handle events from the NimBLE stack.
*/
int NimBLEServer::handleGattEvent(uint16_t conn_handle, uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt, void *arg) {
NIMBLE_LOGD(LOG_TAG, "Gatt %s event", (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR ||
ctxt->op == BLE_GATT_ACCESS_OP_READ_DSC) ? "Read" : "Write");
auto pAtt = static_cast<NimBLELocalValueAttribute*>(arg);
const auto& val = pAtt->getAttVal();
NimBLEConnInfo peerInfo{};
ble_gap_conn_find(conn_handle, &peerInfo.m_desc);
switch(ctxt->op) {
case BLE_GATT_ACCESS_OP_READ_DSC:
case BLE_GATT_ACCESS_OP_READ_CHR: {
// If the packet header is only 8 bytes this is a follow up of a long read
// so we don't want to call the onRead() callback again.
if(ctxt->om->om_pkthdr_len > 8 ||
conn_handle == BLE_HS_CONN_HANDLE_NONE ||
val.size() <= (ble_att_mtu(conn_handle) - 3)) {
pAtt->readEvent(peerInfo);
}
ble_npl_hw_enter_critical();
int rc = os_mbuf_append(ctxt->om, val.data(), val.size());
ble_npl_hw_exit_critical(0);
return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
}
case BLE_GATT_ACCESS_OP_WRITE_DSC:
case BLE_GATT_ACCESS_OP_WRITE_CHR: {
uint16_t att_max_len = val.max_size();
if (ctxt->om->om_len > att_max_len) {
return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
}
uint8_t buf[att_max_len];
uint16_t len = ctxt->om->om_len;
memcpy(buf, ctxt->om->om_data,len);
os_mbuf *next;
next = SLIST_NEXT(ctxt->om, om_next);
while(next != NULL){
if((len + next->om_len) > att_max_len) {
return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
}
memcpy(&buf[len], next->om_data, next->om_len);
len += next->om_len;
next = SLIST_NEXT(next, om_next);
}
pAtt->writeEvent(buf, len, peerInfo);
return 0;
}
default:
break;
}
return BLE_ATT_ERR_UNLIKELY;
} // handleGattEvent
/**
* @brief Set the server callbacks.
*
@@ -633,7 +831,7 @@ void NimBLEServer::removeService(NimBLEService* service, bool deleteSvc) {
// Check if the service was already removed and if so check if this
// is being called to delete the object and do so if requested.
// Otherwise, ignore the call and return.
if(service->m_removed > 0) {
if(service->getRemoved() > 0) {
if(deleteSvc) {
for(auto it = m_svcVec.begin(); it != m_svcVec.end(); ++it) {
if ((*it) == service) {
@@ -652,7 +850,7 @@ void NimBLEServer::removeService(NimBLEService* service, bool deleteSvc) {
return;
}
service->m_removed = deleteSvc ? NIMBLE_ATT_REMOVE_DELETE : NIMBLE_ATT_REMOVE_HIDE;
service->setRemoved(deleteSvc ? NIMBLE_ATT_REMOVE_DELETE : NIMBLE_ATT_REMOVE_HIDE);
serviceChanged();
#if !CONFIG_BT_NIMBLE_EXT_ADV
NimBLEDevice::getAdvertising()->removeServiceUUID(service->getUUID());
@@ -676,12 +874,12 @@ void NimBLEServer::addService(NimBLEService* service) {
// If adding a service that was not removed add it and return.
// Else reset GATT and send service changed notification.
if(service->m_removed == 0) {
if(service->getRemoved() == 0) {
m_svcVec.push_back(service);
return;
}
service->m_removed = 0;
service->setRemoved(0);
serviceChanged();
}
@@ -700,8 +898,8 @@ void NimBLEServer::resetGATT() {
ble_svc_gatt_init();
for(auto it = m_svcVec.begin(); it != m_svcVec.end(); ) {
if ((*it)->m_removed > 0) {
if ((*it)->m_removed == NIMBLE_ATT_REMOVE_DELETE) {
if ((*it)->getRemoved() > 0) {
if ((*it)->getRemoved() == NIMBLE_ATT_REMOVE_DELETE) {
delete *it;
it = m_svcVec.erase(it);
} else {
@@ -857,6 +1055,10 @@ void NimBLEServerCallbacks::onConnect(NimBLEServer* pServer, NimBLEConnInfo& con
NIMBLE_LOGD("NimBLEServerCallbacks", "onConnect(): Default");
} // onConnect
void NimBLEServerCallbacks::onConnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo, std::string& name) {
NIMBLE_LOGD("NimBLEServerCallbacks", "onConnect(): Default");
} // onConnect
void NimBLEServerCallbacks::onDisconnect(NimBLEServer* pServer,
NimBLEConnInfo& connInfo, int reason) {
NIMBLE_LOGD("NimBLEServerCallbacks", "onDisconnect(): Default");
@@ -884,4 +1086,8 @@ void NimBLEServerCallbacks::onAuthenticationComplete(const NimBLEConnInfo& connI
NIMBLE_LOGD("NimBLEServerCallbacks", "onAuthenticationComplete: default");
} // onAuthenticationComplete
void NimBLEServerCallbacks::onAuthenticationComplete(const NimBLEConnInfo& connInfo, const std::string& name){
NIMBLE_LOGD("NimBLEServerCallbacks", "onAuthenticationComplete: default");
} // onAuthenticationComplete
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL */

View File

@@ -18,10 +18,8 @@
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#define NIMBLE_ATT_REMOVE_HIDE 1
#define NIMBLE_ATT_REMOVE_DELETE 2
#define onMtuChanged onMTUChange
class NimBLEServer;
class NimBLEServerCallbacks;
#include "NimBLEUtils.h"
#include "NimBLEAddress.h"
@@ -31,13 +29,13 @@
#include "NimBLEAdvertising.h"
#endif
#include "NimBLEService.h"
#include "NimBLECharacteristic.h"
#include "NimBLEConnInfo.h"
#define NIMBLE_ATT_REMOVE_HIDE 1
#define NIMBLE_ATT_REMOVE_DELETE 2
class NimBLEService;
class NimBLECharacteristic;
class NimBLEServerCallbacks;
#define onMtuChanged onMTUChange
/**
* @brief The model of a %BLE server.
@@ -80,6 +78,8 @@ public:
NimBLEConnInfo getPeerInfo(size_t index);
NimBLEConnInfo getPeerInfo(const NimBLEAddress& address);
NimBLEConnInfo getPeerIDInfo(uint16_t id);
std::string getPeerName(const NimBLEConnInfo& connInfo);
void getPeerNameOnConnect(bool enable);
#if !CONFIG_BT_NIMBLE_EXT_ADV || defined(_DOXYGEN_)
void advertiseOnDisconnect(bool);
#endif
@@ -100,6 +100,7 @@ private:
#if !CONFIG_BT_NIMBLE_EXT_ADV
bool m_advertiseOnDisconnect;
#endif
bool m_getPeerNameOnConnect;
bool m_svcChanged;
NimBLEServerCallbacks* m_pServerCallbacks;
bool m_deleteCallbacks;
@@ -112,10 +113,17 @@ private:
std::vector<NimBLECharacteristic*> m_notifyChrVec;
static int handleGapEvent(struct ble_gap_event *event, void *arg);
static int peerNameCB(uint16_t conn_handle, const struct ble_gatt_error *error,
struct ble_gatt_attr *attr, void *arg);
std::string getPeerNameInternal(uint16_t conn_handle, TaskHandle_t task, int cb_type = -1);
void serviceChanged();
void resetGATT();
bool setIndicateWait(uint16_t conn_handle);
void clearIndicateWait(uint16_t conn_handle);
static int handleGattEvent(uint16_t conn_handle, uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt, void *arg);
}; // NimBLEServer
@@ -130,11 +138,21 @@ public:
* @brief Handle a client connection.
* This is called when a client connects.
* @param [in] pServer A pointer to the %BLE server that received the client connection.
* @param [in] connInfo A reference to a NimBLEConnInfo instance with information
* @param [in] connInfo A reference to a NimBLEConnInfo instance with information.
* about the peer connection parameters.
*/
virtual void onConnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo);
/**
* @brief Handle a client connection.
* This is called when a client connects.
* @param [in] pServer A pointer to the %BLE server that received the client connection.
* @param [in] connInfo A reference to a NimBLEConnInfo instance with information.
* @param [in] name The name of the connected peer device.
* about the peer connection parameters.
*/
virtual void onConnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo, std::string& name);
/**
* @brief Handle a client disconnection.
* This is called when a client discconnects.
@@ -174,6 +192,14 @@ public:
*/
virtual void onAuthenticationComplete(const NimBLEConnInfo& connInfo);
/**
* @brief Called when the pairing procedure is complete.
* @param [in] connInfo A reference to a NimBLEConnInfo instance with information
* @param [in] name The name of the connected peer device.
* about the peer connection parameters.
*/
virtual void onAuthenticationComplete(const NimBLEConnInfo& connInfo, const std::string& name);
/**
* @brief Called when the peer identity address is resolved.
* @param [in] connInfo A reference to a NimBLEConnInfo instance with information

View File

@@ -17,91 +17,67 @@
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#include "NimBLEDevice.h"
#include "NimBLEService.h"
#include "NimBLEUtils.h"
#include "NimBLELog.h"
# include "NimBLEDevice.h"
# include "NimBLEService.h"
# include "NimBLEUtils.h"
# include "NimBLELog.h"
#include <string>
static const char* LOG_TAG = "NimBLEService"; // Tag for logging.
#define NULL_HANDLE (0xffff)
# include <string>
static const char* LOG_TAG = "NimBLEService";
/**
* @brief Construct an instance of the NimBLEService
* @param [in] uuid The UUID of the service.
*/
NimBLEService::NimBLEService(const char* uuid)
: NimBLEService(NimBLEUUID(uuid)) {
}
NimBLEService::NimBLEService(const char* uuid) : NimBLEService(NimBLEUUID(uuid)) {}
/**
* @brief Construct an instance of the BLEService
* @param [in] uuid The UUID of the service.
*/
NimBLEService::NimBLEService(const NimBLEUUID &uuid) {
m_uuid = uuid;
m_handle = NULL_HANDLE;
m_pSvcDef = nullptr;
m_removed = 0;
} // NimBLEService
NimBLEService::NimBLEService(const NimBLEUUID& uuid)
: NimBLELocalAttribute{uuid, 0}, m_pSvcDef{{0, getUUID().getBase(), nullptr, nullptr},{}} {}
/**
* @brief Destructor, make sure we release the resources allocated for the service.
*/
NimBLEService::~NimBLEService() {
if(m_pSvcDef != nullptr) {
if(m_pSvcDef->characteristics != nullptr) {
for(int i=0; m_pSvcDef->characteristics[i].uuid != NULL; ++i) {
if(m_pSvcDef->characteristics[i].descriptors) {
delete(m_pSvcDef->characteristics[i].descriptors);
}
}
delete(m_pSvcDef->characteristics);
if (m_pSvcDef->characteristics) {
if (m_pSvcDef->characteristics->descriptors) {
delete[] m_pSvcDef->characteristics->descriptors;
}
delete(m_pSvcDef);
delete[] m_pSvcDef->characteristics;
}
for(auto &it : m_chrVec) {
for (const auto& it : m_chrVec) {
delete it;
}
}
} // ~NimBLEService
/**
* @brief Dump details of this BLE GATT service.
*/
void NimBLEService::dump() {
NIMBLE_LOGD(LOG_TAG, "Service: uuid:%s, handle: 0x%2x",
m_uuid.toString().c_str(),
m_handle);
void NimBLEService::dump() const {
NIMBLE_LOGD(LOG_TAG, "Service: uuid:%s, handle: 0x%2x", getUUID().toString().c_str(), getHandle());
std::string res;
int count = 0;
char hex[5];
for (auto &it: m_chrVec) {
if (count > 0) {res += "\n";}
int count = 0;
char hex[5];
for (const auto& it : m_chrVec) {
if (count > 0) {
res += "\n";
}
snprintf(hex, sizeof(hex), "%04x", it->getHandle());
count++;
res += "handle: 0x";
res += hex;
res += ", uuid: " + std::string(it->getUUID());
}
NIMBLE_LOGD(LOG_TAG, "Characteristics:\n%s", res.c_str());
} // dump
/**
* @brief Get the UUID of the service.
* @return the UUID of the service.
*/
NimBLEUUID NimBLEService::getUUID() {
return m_uuid;
} // getUUID
/**
* @brief Builds the database of characteristics/descriptors for the service
* and registers it with the NimBLE stack.
@@ -111,149 +87,99 @@ bool NimBLEService::start() {
NIMBLE_LOGD(LOG_TAG, ">> start(): Starting service: %s", toString().c_str());
// Rebuild the service definition if the server attributes have changed.
if(getServer()->m_svcChanged && m_pSvcDef != nullptr) {
if(m_pSvcDef[0].characteristics) {
if(m_pSvcDef[0].characteristics[0].descriptors) {
delete(m_pSvcDef[0].characteristics[0].descriptors);
if (getServer()->m_svcChanged) {
if (m_pSvcDef->characteristics) {
if (m_pSvcDef->characteristics->descriptors) {
delete[] m_pSvcDef->characteristics->descriptors;
}
delete(m_pSvcDef[0].characteristics);
delete[] m_pSvcDef->characteristics;
}
delete(m_pSvcDef);
m_pSvcDef = nullptr;
m_pSvcDef->type = 0;
}
if(m_pSvcDef == nullptr) {
// Nimble requires an array of services to be sent to the api
// Since we are adding 1 at a time we create an array of 2 and set the type
// of the second service to 0 to indicate the end of the array.
ble_gatt_svc_def* svc = new ble_gatt_svc_def[2];
ble_gatt_chr_def* pChr_a = nullptr;
ble_gatt_dsc_def* pDsc_a = nullptr;
svc[0].type = BLE_GATT_SVC_TYPE_PRIMARY;
svc[0].uuid = &m_uuid.getNative()->u;
svc[0].includes = NULL;
int removedCount = 0;
for(auto it = m_chrVec.begin(); it != m_chrVec.end(); ) {
if ((*it)->m_removed > 0) {
if ((*it)->m_removed == NIMBLE_ATT_REMOVE_DELETE) {
delete *it;
it = m_chrVec.erase(it);
} else {
++removedCount;
++it;
}
if (!m_pSvcDef->type) {
m_pSvcDef->type = BLE_GATT_SVC_TYPE_PRIMARY;
size_t numChrs = 0;
for (const auto& chr : m_chrVec) {
if (chr->getRemoved()) {
continue;
}
++it;
++numChrs;
}
size_t numChrs = m_chrVec.size() - removedCount;
NIMBLE_LOGD(LOG_TAG,"Adding %d characteristics for service %s", numChrs, toString().c_str());
NIMBLE_LOGD(LOG_TAG, "Adding %d characteristics for service %s", numChrs, toString().c_str());
if (numChrs) {
int i = 0;
if(!numChrs){
svc[0].characteristics = NULL;
}else{
// Nimble requires the last characteristic to have it's uuid = 0 to indicate the end
// of the characteristics for the service. We create 1 extra and set it to null
// for this purpose.
pChr_a = new ble_gatt_chr_def[numChrs + 1]{};
int i = 0;
for(auto chr_it = m_chrVec.begin(); chr_it != m_chrVec.end(); ++chr_it) {
if((*chr_it)->m_removed > 0) {
ble_gatt_chr_def* pChrs = new ble_gatt_chr_def[numChrs + 1]{};
for (const auto& chr : m_chrVec) {
if (chr->getRemoved()) {
continue;
}
removedCount = 0;
for(auto it = (*chr_it)->m_dscVec.begin(); it != (*chr_it)->m_dscVec.end(); ) {
if ((*it)->m_removed > 0) {
if ((*it)->m_removed == NIMBLE_ATT_REMOVE_DELETE) {
delete *it;
it = (*chr_it)->m_dscVec.erase(it);
} else {
++removedCount;
++it;
}
size_t numDscs = 0;
for (const auto& dsc : chr->m_dscVec) {
if (dsc->getRemoved()) {
continue;
}
++it;
++numDscs;
}
size_t numDscs = (*chr_it)->m_dscVec.size() - removedCount;
if (numDscs) {
int j = 0;
if(!numDscs){
pChr_a[i].descriptors = NULL;
} else {
// Must have last descriptor uuid = 0 so we have to create 1 extra
pDsc_a = new ble_gatt_dsc_def[numDscs+1];
int d = 0;
for(auto dsc_it = (*chr_it)->m_dscVec.begin(); dsc_it != (*chr_it)->m_dscVec.end(); ++dsc_it ) {
if((*dsc_it)->m_removed > 0) {
ble_gatt_dsc_def* pDscs = new ble_gatt_dsc_def[numDscs + 1]{};
for (const auto& dsc : chr->m_dscVec) {
if (dsc->getRemoved()) {
continue;
}
pDsc_a[d].uuid = &(*dsc_it)->m_uuid.getNative()->u;
pDsc_a[d].att_flags = (*dsc_it)->m_properties;
pDsc_a[d].min_key_size = 0;
pDsc_a[d].access_cb = NimBLEDescriptor::handleGapEvent;
pDsc_a[d].arg = (*dsc_it);
++d;
printf("adding disc %s\n", dsc->toString().c_str());
pDscs[j].uuid = dsc->getUUID().getBase();
pDscs[j].att_flags = dsc->getProperties();
pDscs[j].min_key_size = 0;
pDscs[j].access_cb = NimBLEServer::handleGattEvent;
pDscs[j].arg = dsc;
++j;
}
pDsc_a[numDscs].uuid = NULL;
pChr_a[i].descriptors = pDsc_a;
pChrs[i].descriptors = pDscs;
}
pChr_a[i].uuid = &(*chr_it)->m_uuid.getNative()->u;
pChr_a[i].access_cb = NimBLECharacteristic::handleGapEvent;
pChr_a[i].arg = (*chr_it);
pChr_a[i].flags = (*chr_it)->m_properties;
pChr_a[i].min_key_size = 0;
pChr_a[i].val_handle = &(*chr_it)->m_handle;
pChrs[i].uuid = chr->getUUID().getBase();
pChrs[i].access_cb = NimBLEServer::handleGattEvent;
pChrs[i].arg = chr;
pChrs[i].flags = chr->getProperties();
pChrs[i].min_key_size = 0;
pChrs[i].val_handle = &chr->m_handle;
++i;
}
pChr_a[numChrs].uuid = NULL;
svc[0].characteristics = pChr_a;
m_pSvcDef->characteristics = pChrs;
}
// end of services must indicate to api with type = 0
svc[1].type = 0;
m_pSvcDef = svc;
}
int rc = ble_gatts_count_cfg((const ble_gatt_svc_def*)m_pSvcDef);
int rc = ble_gatts_count_cfg(m_pSvcDef);
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "ble_gatts_count_cfg failed, rc= %d, %s", rc, NimBLEUtils::returnCodeToString(rc));
return false;
}
rc = ble_gatts_add_svcs((const ble_gatt_svc_def*)m_pSvcDef);
rc = ble_gatts_add_svcs(m_pSvcDef);
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "ble_gatts_add_svcs, rc= %d, %s", rc, NimBLEUtils::returnCodeToString(rc));
return false;
}
NIMBLE_LOGD(LOG_TAG, "<< start()");
return true;
} // start
/**
* @brief Get the handle associated with this service.
* @return The handle associated with this service.
*/
uint16_t NimBLEService::getHandle() {
if (m_handle == NULL_HANDLE) {
ble_gatts_find_svc(&getUUID().getNative()->u, &m_handle);
}
return m_handle;
} // getHandle
/**
* @brief Create a new BLE Characteristic associated with this service.
* @param [in] uuid - The UUID of the characteristic.
@@ -263,8 +189,7 @@ uint16_t NimBLEService::getHandle() {
*/
NimBLECharacteristic* NimBLEService::createCharacteristic(const char* uuid, uint32_t properties, uint16_t max_len) {
return createCharacteristic(NimBLEUUID(uuid), properties, max_len);
}
} // createCharacteristic
/**
* @brief Create a new BLE Characteristic associated with this service.
@@ -273,36 +198,32 @@ NimBLECharacteristic* NimBLEService::createCharacteristic(const char* uuid, uint
* @param [in] max_len - The maximum length in bytes that the characteristic value can hold.
* @return The new BLE characteristic.
*/
NimBLECharacteristic* NimBLEService::createCharacteristic(const NimBLEUUID &uuid, uint32_t properties, uint16_t max_len) {
NimBLECharacteristic* NimBLEService::createCharacteristic(const NimBLEUUID& uuid, uint32_t properties, uint16_t max_len) {
NimBLECharacteristic* pCharacteristic = new NimBLECharacteristic(uuid, properties, max_len, this);
if (getCharacteristic(uuid) != nullptr) {
NIMBLE_LOGD(LOG_TAG, "<< Adding a duplicate characteristic with UUID: %s",
std::string(uuid).c_str());
NIMBLE_LOGD(LOG_TAG, "Adding a duplicate characteristic with UUID: %s", std::string(uuid).c_str());
}
addCharacteristic(pCharacteristic);
return pCharacteristic;
} // createCharacteristic
/**
* @brief Add a characteristic to the service.
* @param[in] pCharacteristic A pointer to the characteristic instance to add to the service.
*/
void NimBLEService::addCharacteristic(NimBLECharacteristic* pCharacteristic) {
bool foundRemoved = false;
if(pCharacteristic->m_removed > 0) {
for(auto& it : m_chrVec) {
if(it == pCharacteristic) {
if (pCharacteristic->getRemoved() > 0) {
for (const auto& chr : m_chrVec) {
if (chr == pCharacteristic) {
foundRemoved = true;
pCharacteristic->m_removed = 0;
pCharacteristic->setRemoved(0);
}
}
}
if(!foundRemoved) {
if (!foundRemoved) {
m_chrVec.push_back(pCharacteristic);
}
@@ -310,7 +231,6 @@ void NimBLEService::addCharacteristic(NimBLECharacteristic* pCharacteristic) {
getServer()->serviceChanged();
} // addCharacteristic
/**
* @brief Remove a characteristic from the service.
* @param[in] pCharacteristic A pointer to the characteristic instance to remove from the service.
@@ -320,12 +240,12 @@ void NimBLEService::removeCharacteristic(NimBLECharacteristic* pCharacteristic,
// Check if the characteristic was already removed and if so, check if this
// is being called to delete the object and do so if requested.
// Otherwise, ignore the call and return.
if(pCharacteristic->m_removed > 0) {
if(deleteChr) {
for(auto it = m_chrVec.begin(); it != m_chrVec.end(); ++it) {
if (pCharacteristic->getRemoved() > 0) {
if (deleteChr) {
for (auto it = m_chrVec.begin(); it != m_chrVec.end(); ++it) {
if ((*it) == pCharacteristic) {
delete (*it);
m_chrVec.erase(it);
delete *it;
break;
}
}
@@ -334,20 +254,19 @@ void NimBLEService::removeCharacteristic(NimBLECharacteristic* pCharacteristic,
return;
}
pCharacteristic->m_removed = deleteChr ? NIMBLE_ATT_REMOVE_DELETE : NIMBLE_ATT_REMOVE_HIDE;
pCharacteristic->setRemoved(deleteChr ? NIMBLE_ATT_REMOVE_DELETE : NIMBLE_ATT_REMOVE_HIDE);
getServer()->serviceChanged();
} // removeCharacteristic
/**
* @brief Get a pointer to the characteristic object with the specified UUID.
* @param [in] uuid The UUID of the characteristic.
* @param instanceId The index of the characteristic to return (used when multiple characteristics have the same UUID).
* @return A pointer to the characteristic object or nullptr if not found.
*/
NimBLECharacteristic* NimBLEService::getCharacteristic(const char* uuid, uint16_t instanceId) {
NimBLECharacteristic* NimBLEService::getCharacteristic(const char* uuid, uint16_t instanceId) const {
return getCharacteristic(NimBLEUUID(uuid), instanceId);
}
} // getCharacteristic
/**
* @brief Get a pointer to the characteristic object with the specified UUID.
@@ -355,59 +274,62 @@ NimBLECharacteristic* NimBLEService::getCharacteristic(const char* uuid, uint16_
* @param instanceId The index of the characteristic to return (used when multiple characteristics have the same UUID).
* @return A pointer to the characteristic object or nullptr if not found.
*/
NimBLECharacteristic* NimBLEService::getCharacteristic(const NimBLEUUID &uuid, uint16_t instanceId) {
NimBLECharacteristic* NimBLEService::getCharacteristic(const NimBLEUUID& uuid, uint16_t instanceId) const {
uint16_t position = 0;
for (auto &it : m_chrVec) {
if (it->getUUID() == uuid) {
for (const auto& chr : m_chrVec) {
if (chr->getUUID() == uuid) {
if (position == instanceId) {
return it;
return chr;
}
position++;
}
}
return nullptr;
}
} // getCharacteristic
/**
* @brief Get a pointer to the characteristic object with the specified handle.
* @param handle The handle of the characteristic.
* @return A pointer to the characteristic object or nullptr if not found.
*/
NimBLECharacteristic *NimBLEService::getCharacteristicByHandle(uint16_t handle) {
for (auto &it : m_chrVec) {
if (it->getHandle() == handle) {
return it;
NimBLECharacteristic* NimBLEService::getCharacteristicByHandle(uint16_t handle) const {
for (const auto& chr : m_chrVec) {
if (chr->getHandle() == handle) {
return chr;
}
}
return nullptr;
}
} // getCharacteristicByHandle
/**
* @return A vector containing pointers to each characteristic associated with this service.
*/
std::vector<NimBLECharacteristic *> NimBLEService::getCharacteristics() {
const std::vector<NimBLECharacteristic*>& NimBLEService::getCharacteristics() const {
return m_chrVec;
}
} // getCharacteristics
/**
* @return A vector containing pointers to each characteristic with the provided UUID associated with this service.
*/
std::vector<NimBLECharacteristic *> NimBLEService::getCharacteristics(const char *uuid) {
std::vector<NimBLECharacteristic*> NimBLEService::getCharacteristics(const char* uuid) const {
return getCharacteristics(NimBLEUUID(uuid));
}
} // getCharacteristics
/**
* @return A vector containing pointers to each characteristic with the provided UUID associated with this service.
*/
std::vector<NimBLECharacteristic *> NimBLEService::getCharacteristics(const NimBLEUUID &uuid) {
std::vector<NimBLECharacteristic*> NimBLEService::getCharacteristics(const NimBLEUUID& uuid) const {
std::vector<NimBLECharacteristic*> result;
for (auto &it : m_chrVec) {
if (it->getUUID() == uuid) {
result.push_back(it);
for (const auto& chr : m_chrVec) {
if (chr->getUUID() == uuid) {
result.push_back(chr);
}
}
return result;
}
} // getCharacteristics
/**
* @brief Return a string representation of this service.
@@ -416,31 +338,29 @@ std::vector<NimBLECharacteristic *> NimBLEService::getCharacteristics(const NimB
* * Its handle
* @return A string representation of this service.
*/
std::string NimBLEService::toString() {
std::string NimBLEService::toString() const {
std::string res = "UUID: " + getUUID().toString();
char hex[5];
char hex[5];
snprintf(hex, sizeof(hex), "%04x", getHandle());
res += ", handle: 0x";
res += hex;
return res;
} // toString
/**
* @brief Get the BLE server associated with this service.
* @return The BLEServer associated with this service.
*/
NimBLEServer* NimBLEService::getServer() {
NimBLEServer* NimBLEService::getServer() const {
return NimBLEDevice::getServer();
}// getServer
} // getServer
/**
* @brief Checks if the service has been started.
* @return True if the service has been started.
*/
bool NimBLEService::isStarted() {
return m_pSvcDef != nullptr;
}
bool NimBLEService::isStarted() const {
return m_pSvcDef->type > 0;
} // isStarted
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL */

View File

@@ -12,76 +12,59 @@
* Author: kolban
*/
#ifndef MAIN_NIMBLESERVICE_H_
#define MAIN_NIMBLESERVICE_H_
#ifndef NIMBLE_CPP_SERVICE_H_
#define NIMBLE_CPP_SERVICE_H_
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#include "NimBLEServer.h"
#include "NimBLECharacteristic.h"
#include "NimBLEUUID.h"
class NimBLEServer;
class NimBLECharacteristic;
class NimBLEService;
# include "NimBLEAttribute.h"
# include "NimBLEServer.h"
# include "NimBLECharacteristic.h"
/**
* @brief The model of a %BLE service.
* @brief The model of a BLE service.
*
*/
class NimBLEService {
public:
class NimBLEService : public NimBLELocalAttribute {
public:
NimBLEService(const char* uuid);
NimBLEService(const NimBLEUUID &uuid);
NimBLEService(const NimBLEUUID& uuid);
~NimBLEService();
NimBLEServer* getServer();
NimBLEUUID getUUID();
uint16_t getHandle();
std::string toString();
void dump();
bool isStarted();
NimBLEServer* getServer() const;
std::string toString() const;
void dump() const;
bool isStarted() const;
bool start();
NimBLECharacteristic* createCharacteristic(const char* uuid,
uint32_t properties =
NIMBLE_PROPERTY::READ |
NIMBLE_PROPERTY::WRITE,
uint16_t max_len = BLE_ATT_ATTR_MAX_LEN);
NimBLECharacteristic* createCharacteristic(const NimBLEUUID &uuid,
uint32_t properties =
NIMBLE_PROPERTY::READ |
NIMBLE_PROPERTY::WRITE,
uint16_t max_len = BLE_ATT_ATTR_MAX_LEN);
uint32_t properties = NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE,
uint16_t max_len = BLE_ATT_ATTR_MAX_LEN);
NimBLECharacteristic* createCharacteristic(const NimBLEUUID& uuid,
uint32_t properties = NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE,
uint16_t max_len = BLE_ATT_ATTR_MAX_LEN);
void addCharacteristic(NimBLECharacteristic* pCharacteristic);
void removeCharacteristic(NimBLECharacteristic* pCharacteristic, bool deleteChr = false);
NimBLECharacteristic* getCharacteristic(const char* uuid, uint16_t instanceId = 0);
NimBLECharacteristic* getCharacteristic(const NimBLEUUID &uuid, uint16_t instanceId = 0);
NimBLECharacteristic* getCharacteristicByHandle(uint16_t handle);
NimBLECharacteristic* getCharacteristic(const char* uuid, uint16_t instanceId = 0) const;
NimBLECharacteristic* getCharacteristic(const NimBLEUUID& uuid, uint16_t instanceId = 0) const;
NimBLECharacteristic* getCharacteristicByHandle(uint16_t handle) const;
std::vector<NimBLECharacteristic*> getCharacteristics();
std::vector<NimBLECharacteristic*> getCharacteristics(const char* uuid);
std::vector<NimBLECharacteristic*> getCharacteristics(const NimBLEUUID &uuid);
const std::vector<NimBLECharacteristic*>& getCharacteristics() const;
std::vector<NimBLECharacteristic*> getCharacteristics(const char* uuid) const;
std::vector<NimBLECharacteristic*> getCharacteristics(const NimBLEUUID& uuid) const;
private:
friend class NimBLEServer;
private:
friend class NimBLEServer;
friend class NimBLEDevice;
uint16_t m_handle;
NimBLEUUID m_uuid;
ble_gatt_svc_def* m_pSvcDef;
uint8_t m_removed;
std::vector<NimBLECharacteristic*> m_chrVec;
std::vector<NimBLECharacteristic*> m_chrVec{};
// Nimble requires an array of services to be sent to the api
// Since we are adding 1 at a time we create an array of 2 and set the type
// of the second service to 0 to indicate the end of the array.
ble_gatt_svc_def m_pSvcDef[2]{};
}; // NimBLEService
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL */
#endif /* MAIN_NIMBLESERVICE_H_ */
#endif /* NIMBLE_CPP_SERVICE_H_ */

View File

@@ -15,21 +15,28 @@
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include "NimBLEUtils.h"
#include "NimBLEUUID.h"
#include "NimBLELog.h"
# include "NimBLEUtils.h"
# include "NimBLEUUID.h"
# include "NimBLELog.h"
#include <algorithm>
# include <algorithm>
static const char* LOG_TAG = "NimBLEUUID";
static const char* LOG_TAG = "NimBLEUUID";
static const uint8_t ble_base_uuid[] = {
0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
/**
* @brief Create a UUID from the native UUID.
* @param [in] uuid The native UUID.
*/
NimBLEUUID::NimBLEUUID(const ble_uuid_any_t& uuid) : m_uuid{uuid} {}
/**
* @brief Create a UUID from a string.
*
* Create a UUID from a string. There will be two possible stories here. Either the string represents
* a binary data field or the string represents a hex encoding of a UUID.
* For the hex encoding, here is an example:
* Create a UUID from a string. There will be two possible stories here. Either
* the string represents a binary data field or the string represents a hex
* encoding of a UUID. For the hex encoding, here is an example:
*
* ```
* "beb5483e-36e1-4688-b7f5-ea07361b26a8"
@@ -41,104 +48,68 @@ static const char* LOG_TAG = "NimBLEUUID";
*
* @param [in] value The string to build a UUID from.
*/
NimBLEUUID::NimBLEUUID(const std::string &value) {
m_valueSet = true;
NimBLEUUID::NimBLEUUID(const std::string& value) {
if (value.length() == 4) {
m_uuid.u.type = BLE_UUID_TYPE_16;
m_uuid.u16.value = strtoul(value.c_str(), NULL, 16);
}
else if (value.length() == 8) {
m_uuid.u.type = BLE_UUID_TYPE_32;
m_uuid.u32.value = strtoul(value.c_str(), NULL, 16);
}
else if (value.length() == 16) {
*this = NimBLEUUID((uint8_t*)value.data(), 16, true);
}
else if (value.length() == 36) {
// If the length of the string is 36 bytes then we will assume it is a long hex string in
// UUID format.
char * position = const_cast<char *>(value.c_str());
uint32_t first = strtoul(position, &position, 16);
uint16_t second = strtoul(position + 1, &position, 16);
uint16_t third = strtoul(position + 1, &position, 16);
uint16_t fourth = strtoul(position + 1, &position, 16);
uint64_t fifth = strtoull(position + 1, NULL, 16);
*this = NimBLEUUID(first, second, third, (uint64_t(fourth) << 48) + fifth);
}
else {
m_valueSet = false;
m_uuid.u.type = BLE_UUID_TYPE_16;
m_uuid.u16.value = strtoul(value.c_str(), nullptr, 16);
} else if (value.length() == 8) {
m_uuid.u.type = BLE_UUID_TYPE_32;
m_uuid.u32.value = strtoul(value.c_str(), nullptr, 16);
} else if (value.length() == 16) {
memcpy(m_uuid.u128.value, &value[0], 16);
m_uuid.u.type = BLE_UUID_TYPE_128;
} else if (value.length() == 36) {
char* position;
uint64_t first_half = (strtoull(&value[0], &position, 16) << 32) +
(strtoull(position + 1, &position, 16) << 16) + strtoull(position + 1, &position, 16);
uint64_t second_half = (strtoull(position + 1, &position, 16) << 48) + strtoull(position + 1, nullptr, 16);
memcpy(m_uuid.u128.value + 8, &first_half, 8);
memcpy(m_uuid.u128.value, &second_half, 8);
m_uuid.u.type = BLE_UUID_TYPE_128;
} else {
NIMBLE_LOGE(LOG_TAG, "Invalid UUID length");
m_uuid.u.type = 0;
}
} // NimBLEUUID(std::string)
/**
* @brief Create a UUID from 2, 4, 16 bytes of memory.
* @param [in] pData The pointer to the start of the UUID.
* @param [in] size The size of the data.
* @param [in] msbFirst Is the MSB first in pData memory?
*/
NimBLEUUID::NimBLEUUID(const uint8_t* pData, size_t size, bool msbFirst) {
uint8_t *uuidValue = nullptr;
switch(size) {
case 2:
uuidValue = (uint8_t*)&m_uuid.u16.value;
m_uuid.u.type = BLE_UUID_TYPE_16;
break;
case 4:
uuidValue = (uint8_t*)&m_uuid.u32.value;
m_uuid.u.type = BLE_UUID_TYPE_32;
break;
case 16:
uuidValue = m_uuid.u128.value;
m_uuid.u.type = BLE_UUID_TYPE_128;
break;
default:
m_valueSet = false;
NIMBLE_LOGE(LOG_TAG, "Invalid UUID size");
return;
NimBLEUUID::NimBLEUUID(const uint8_t* pData, size_t size) {
if (ble_uuid_init_from_buf(&m_uuid, pData, size)) {
NIMBLE_LOGE(LOG_TAG, "Invalid UUID size");
m_uuid.u.type = 0;
}
if (msbFirst) {
std::reverse_copy(pData, pData + size, uuidValue);
} else {
memcpy(uuidValue, pData, size);
}
m_valueSet = true;
} // NimBLEUUID
} // NimBLEUUID(const uint8_t* pData, size_t size)
/**
* @brief Create a UUID from the 16bit value.
* @param [in] uuid The 16bit short form UUID.
*/
NimBLEUUID::NimBLEUUID(uint16_t uuid) {
m_uuid.u.type = BLE_UUID_TYPE_16;
m_uuid.u16.value = uuid;
m_valueSet = true;
} // NimBLEUUID
m_uuid.u.type = BLE_UUID_TYPE_16;
m_uuid.u16.value = uuid;
} // NimBLEUUID(uint16_t uuid)
/**
* @brief Create a UUID from the 32bit value.
* @param [in] uuid The 32bit short form UUID.
*/
NimBLEUUID::NimBLEUUID(uint32_t uuid) {
m_uuid.u.type = BLE_UUID_TYPE_32;
m_uuid.u32.value = uuid;
m_valueSet = true;
} // NimBLEUUID
m_uuid.u.type = BLE_UUID_TYPE_32;
m_uuid.u32.value = uuid;
} // NimBLEUUID(uint32_t uuid)
/**
* @brief Create a UUID from the native UUID.
* @param [in] uuid The native UUID.
*/
NimBLEUUID::NimBLEUUID(const ble_uuid128_t* uuid) {
m_uuid.u.type = BLE_UUID_TYPE_128;
m_uuid.u.type = BLE_UUID_TYPE_128;
memcpy(m_uuid.u128.value, uuid->value, 16);
m_valueSet = true;
} // NimBLEUUID
} // NimBLEUUID(const ble_uuid128_t* uuid)
/**
* @brief Create a UUID from the 128bit value using hex parts instead of string,
@@ -151,32 +122,47 @@ NimBLEUUID::NimBLEUUID(const ble_uuid128_t* uuid) {
* @param [in] fourth The last 64bit of the UUID, combining the last 2 parts of the string equivalent
*/
NimBLEUUID::NimBLEUUID(uint32_t first, uint16_t second, uint16_t third, uint64_t fourth) {
m_uuid.u.type = BLE_UUID_TYPE_128;
memcpy(m_uuid.u128.value + 12, &first, 4);
m_uuid.u.type = BLE_UUID_TYPE_128;
memcpy(m_uuid.u128.value + 12, &first, 4);
memcpy(m_uuid.u128.value + 10, &second, 2);
memcpy(m_uuid.u128.value + 8, &third, 2);
memcpy(m_uuid.u128.value, &fourth, 8);
m_valueSet = true;
}
memcpy(m_uuid.u128.value + 8, &third, 2);
memcpy(m_uuid.u128.value, &fourth, 8);
} // NimBLEUUID(uint32_t first, uint16_t second, uint16_t third, uint64_t fourth)
/**
* @brief Creates an empty UUID.
*/
NimBLEUUID::NimBLEUUID() {
m_valueSet = false;
} // NimBLEUUID
/**
* @brief Get the number of bits in this uuid.
* @return The number of bits in the UUID. One of 16, 32 or 128.
* @brief Get the bit size of the UUID, 16, 32 or 128.
* @return The bit size of the UUID or 0 if not initialized.
*/
uint8_t NimBLEUUID::bitSize() const {
if (!m_valueSet) return 0;
return m_uuid.u.type;
return this->m_uuid.u.type;
} // bitSize
/**
* @brief Get the uuid value.
* @return A pointer to the UUID value or nullptr if not initialized.
* @note This should be checked with `bitSize()` before accessing.
*/
const uint8_t* NimBLEUUID::getValue() const {
switch (bitSize()) {
case BLE_UUID_TYPE_16:
return reinterpret_cast<const uint8_t*>(&m_uuid.u16.value);
case BLE_UUID_TYPE_32:
return reinterpret_cast<const uint8_t*>(&m_uuid.u32.value);
case BLE_UUID_TYPE_128:
return m_uuid.u128.value;
default:
return nullptr;
}
} // getValue
/**
* @brief Get a pointer to the NimBLE UUID base structure.
* @return A Read-only pointer to the NimBLE UUID base structure.
* @note The type value should be checked, if no 16, 32 or 128 then the UUID is not initialized.
*/
const ble_uuid_t* NimBLEUUID::getBase() const {
return &this->m_uuid.u;
} // getBase
/**
* @brief Compare a UUID against this UUID.
@@ -184,10 +170,9 @@ uint8_t NimBLEUUID::bitSize() const {
* @param [in] uuid The UUID to compare against.
* @return True if the UUIDs are equal and false otherwise.
*/
bool NimBLEUUID::equals(const NimBLEUUID &uuid) const {
bool NimBLEUUID::equals(const NimBLEUUID& uuid) const {
return *this == uuid;
}
} // equals
/**
* Create a NimBLEUUID from a string of the form:
@@ -200,14 +185,14 @@ bool NimBLEUUID::equals(const NimBLEUUID &uuid) const {
*
* @param [in] uuid The string to create the UUID from.
*/
NimBLEUUID NimBLEUUID::fromString(const std::string &uuid) {
NimBLEUUID NimBLEUUID::fromString(const std::string& uuid) {
uint8_t start = 0;
if (strstr(uuid.c_str(), "0x") != nullptr) { // If the string starts with 0x, skip those characters.
start = 2;
}
uint8_t len = uuid.length() - start; // Calculate the length of the string we are going to use.
if(len == 4) {
uint8_t len = uuid.length() - start; // Calculate the length of the string we are going to use.
if (len == 4) {
uint16_t x = strtoul(uuid.substr(start, len).c_str(), NULL, 16);
return NimBLEUUID(x);
} else if (len == 8) {
@@ -216,47 +201,29 @@ NimBLEUUID NimBLEUUID::fromString(const std::string &uuid) {
} else if (len == 36) {
return NimBLEUUID(uuid);
}
return NimBLEUUID();
} // fromString
/**
* @brief Get the native UUID value.
* @return The native UUID value or nullptr if not set.
*/
const ble_uuid_any_t* NimBLEUUID::getNative() const {
if (m_valueSet == false) {
NIMBLE_LOGD(LOG_TAG,"<< Return of un-initialized UUID!");
return nullptr;
}
return &m_uuid;
} // getNative
/**
* @brief Convert a UUID to its 128 bit representation.
* @details A UUID can be internally represented as 16bit, 32bit or the full 128bit.
* This method will convert 16 or 32bit representations to the full 128bit.
* @return The NimBLEUUID converted to 128bit.
*/
const NimBLEUUID &NimBLEUUID::to128() {
const NimBLEUUID& NimBLEUUID::to128() {
// If we either don't have a value or are already a 128 bit UUID, nothing further to do.
if (!m_valueSet || m_uuid.u.type == BLE_UUID_TYPE_128) {
return *this;
}
// If we are 16 bit or 32 bit, then set the other bytes of the UUID.
if (m_uuid.u.type == BLE_UUID_TYPE_16) {
*this = NimBLEUUID(m_uuid.u16.value, 0x0000, 0x1000, 0x800000805f9b34fb);
}
else if (m_uuid.u.type == BLE_UUID_TYPE_32) {
*this = NimBLEUUID(m_uuid.u32.value, 0x0000, 0x1000, 0x800000805f9b34fb);
if (bitSize() != BLE_UUID_TYPE_128) {
uint32_t val = bitSize() == BLE_UUID_TYPE_16 ? *reinterpret_cast<const uint16_t*>(getValue())
: *reinterpret_cast<const uint32_t*>(getValue());
memcpy(m_uuid.u128.value, &ble_base_uuid, sizeof(ble_base_uuid) - 4);
memcpy(m_uuid.u128.value + 12, &val, 4);
m_uuid.u.type = BLE_UUID_TYPE_128;
}
return *this;
} // to128
/**
* @brief Convert 128 bit UUID to its 16 bit representation.
* @details A UUID can be internally represented as 16bit, 32bit or the full 128bit.
@@ -264,21 +231,16 @@ const NimBLEUUID &NimBLEUUID::to128() {
* @return The NimBLEUUID converted to 16bit if successful, otherwise the original uuid.
*/
const NimBLEUUID& NimBLEUUID::to16() {
if (!m_valueSet || m_uuid.u.type == BLE_UUID_TYPE_16) {
return *this;
}
if (m_uuid.u.type == BLE_UUID_TYPE_128) {
uint8_t base128[] = {0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00,
0x00, 0x80, 0x00, 0x10, 0x00, 0x00};
if (memcmp(m_uuid.u128.value, base128, sizeof(base128)) == 0 ) {
*this = NimBLEUUID(*(uint16_t*)(m_uuid.u128.value + 12));
if (bitSize() == BLE_UUID_TYPE_128) {
const uint8_t* val = getValue();
if (memcmp(val, ble_base_uuid, sizeof(ble_base_uuid) - 4) == 0) {
m_uuid.u16.value = *reinterpret_cast<const uint16_t*>(val + 12);
m_uuid.u.type = BLE_UUID_TYPE_16;
}
}
return *this;
}
} // to16
/**
* @brief Get a string representation of the UUID.
@@ -295,53 +257,67 @@ std::string NimBLEUUID::toString() const {
return std::string(*this);
} // toString
/**
* @brief Reverse the byte order of the UUID.
* @return The NimBLEUUID with the byte order reversed.
* @details This is useful when comparing UUIDs or when the advertisement data is reversed.
*/
const NimBLEUUID& NimBLEUUID::reverseByteOrder() {
if (bitSize() == BLE_UUID_TYPE_128) {
std::reverse(m_uuid.u128.value, m_uuid.u128.value + 16);
} else if (bitSize() == BLE_UUID_TYPE_32) {
m_uuid.u32.value = __builtin_bswap32(m_uuid.u32.value);
} else if (bitSize() == BLE_UUID_TYPE_16) {
m_uuid.u16.value = __builtin_bswap16(m_uuid.u16.value);
}
return *this;
} // reverseByteOrder
/**
* @brief Convenience operator to check if this UUID is equal to another.
*/
bool NimBLEUUID::operator ==(const NimBLEUUID & rhs) const {
if(m_valueSet && rhs.m_valueSet) {
if(m_uuid.u.type != rhs.m_uuid.u.type) {
uint8_t uuidBase[16] = {
0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80,
0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
if(m_uuid.u.type == BLE_UUID_TYPE_128){
if(rhs.m_uuid.u.type == BLE_UUID_TYPE_16){
memcpy(uuidBase+12, &rhs.m_uuid.u16.value, 2);
} else if (rhs.m_uuid.u.type == BLE_UUID_TYPE_32){
memcpy(uuidBase+12, &rhs.m_uuid.u32.value, 4);
}
return memcmp(m_uuid.u128.value,uuidBase,16) == 0;
} else if(rhs.m_uuid.u.type == BLE_UUID_TYPE_128) {
if(m_uuid.u.type == BLE_UUID_TYPE_16){
memcpy(uuidBase+12, &m_uuid.u16.value, 2);
} else if (m_uuid.u.type == BLE_UUID_TYPE_32){
memcpy(uuidBase+12, &m_uuid.u32.value, 4);
}
return memcmp(rhs.m_uuid.u128.value,uuidBase,16) == 0;
} else {
return false;
}
}
return ble_uuid_cmp(&m_uuid.u, &rhs.m_uuid.u) == 0;
bool NimBLEUUID::operator==(const NimBLEUUID& rhs) const {
if (!this->bitSize() || !rhs.bitSize()) {
return false;
}
return m_valueSet == rhs.m_valueSet;
}
if (this->bitSize() != rhs.bitSize()) {
uint8_t uuid128[sizeof(ble_base_uuid)];
memcpy(uuid128, &ble_base_uuid, sizeof(ble_base_uuid));
if (this->bitSize() == BLE_UUID_TYPE_128) {
memcpy(uuid128 + 12, rhs.getValue(), rhs.bitSize() == BLE_UUID_TYPE_16 ? 2 : 4);
return memcmp(this->getValue(), uuid128, sizeof(ble_base_uuid)) == 0;
} else if (rhs.bitSize() == BLE_UUID_TYPE_128) {
memcpy(uuid128 + 12, getValue(), this->bitSize() == BLE_UUID_TYPE_16 ? 2 : 4);
return memcmp(rhs.getValue(), uuid128, sizeof(ble_base_uuid)) == 0;
} else {
return false;
}
}
if (this->bitSize() == BLE_UUID_TYPE_16) {
return *reinterpret_cast<const uint16_t*>(this->getValue()) == *reinterpret_cast<const uint16_t*>(rhs.getValue());
}
if (this->bitSize() == BLE_UUID_TYPE_32) {
return *reinterpret_cast<const uint32_t*>(this->getValue()) == *reinterpret_cast<const uint32_t*>(rhs.getValue());
}
if (this->bitSize() == BLE_UUID_TYPE_128) {
return memcmp(this->getValue(), rhs.getValue(), 16) == 0;
}
return false;
} // operator==
/**
* @brief Convenience operator to check if this UUID is not equal to another.
*/
bool NimBLEUUID::operator !=(const NimBLEUUID & rhs) const {
bool NimBLEUUID::operator!=(const NimBLEUUID& rhs) const {
return !this->operator==(rhs);
}
} // operator!=
/**
* @brief Convenience operator to convert this UUID to string representation.
@@ -349,12 +325,8 @@ bool NimBLEUUID::operator !=(const NimBLEUUID & rhs) const {
* that accept std::string and/or or it's methods as a parameter.
*/
NimBLEUUID::operator std::string() const {
if (!m_valueSet) return std::string(); // If we have no value, nothing to format.
char buf[BLE_UUID_STR_LEN];
return ble_uuid_to_str(&m_uuid.u, buf);
}
} // operator std::string
#endif /* CONFIG_BT_ENABLED */
# endif /* CONFIG_BT_ENABLED */

View File

@@ -12,53 +12,60 @@
* Author: kolban
*/
#ifndef COMPONENTS_NIMBLEUUID_H_
#define COMPONENTS_NIMBLEUUID_H_
#ifndef NIMBLE_CPP_UUID_H_
#define NIMBLE_CPP_UUID_H_
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED)
#if defined(CONFIG_NIMBLE_CPP_IDF)
#include "host/ble_uuid.h"
#else
#include "nimble/nimble/host/include/host/ble_uuid.h"
#endif
# if defined(CONFIG_NIMBLE_CPP_IDF)
# include "host/ble_uuid.h"
# else
# include "nimble/nimble/host/include/host/ble_uuid.h"
# endif
/**** FIX COMPILATION ****/
#undef min
#undef max
# undef min
# undef max
/**************************/
#include <string>
# include <string>
# include <cstring>
/**
* @brief A model of a %BLE UUID.
*/
class NimBLEUUID {
public:
NimBLEUUID(const std::string &uuid);
public:
/**
* @brief Created a blank UUID.
*/
NimBLEUUID() = default;
NimBLEUUID(const ble_uuid_any_t& uuid);
NimBLEUUID(const std::string& uuid);
NimBLEUUID(uint16_t uuid);
NimBLEUUID(uint32_t uuid);
NimBLEUUID(const ble_uuid128_t* uuid);
NimBLEUUID(const uint8_t* pData, size_t size, bool msbFirst);
NimBLEUUID(const uint8_t* pData, size_t size);
NimBLEUUID(uint32_t first, uint16_t second, uint16_t third, uint64_t fourth);
NimBLEUUID();
uint8_t bitSize() const;
bool equals(const NimBLEUUID &uuid) const;
const ble_uuid_any_t* getNative() const;
const NimBLEUUID & to128();
const NimBLEUUID& to16();
std::string toString() const;
static NimBLEUUID fromString(const std::string &uuid);
uint8_t bitSize() const;
const uint8_t* getValue() const;
const ble_uuid_t* getBase() const;
bool equals(const NimBLEUUID& uuid) const;
std::string toString() const;
static NimBLEUUID fromString(const std::string& uuid);
const NimBLEUUID& to128();
const NimBLEUUID& to16();
const NimBLEUUID& reverseByteOrder();
bool operator ==(const NimBLEUUID & rhs) const;
bool operator !=(const NimBLEUUID & rhs) const;
operator std::string() const;
bool operator==(const NimBLEUUID& rhs) const;
bool operator!=(const NimBLEUUID& rhs) const;
operator std::string() const;
private:
ble_uuid_any_t m_uuid;
bool m_valueSet = false;
private:
ble_uuid_any_t m_uuid{};
}; // NimBLEUUID
#endif /* CONFIG_BT_ENABLED */
#endif /* COMPONENTS_NIMBLEUUID_H_ */
#endif /* NIMBLE_CPP_UUID_H_ */

View File

@@ -16,45 +16,6 @@
static const char* LOG_TAG = "NimBLEUtils";
/**
* @brief A function for checking validity of connection parameters.
* @param [in] params A pointer to the structure containing the parameters to check.
* @return valid == 0 or error code.
*/
int NimBLEUtils::checkConnParams(ble_gap_conn_params* params) {
/* Check connection interval min */
if ((params->itvl_min < BLE_HCI_CONN_ITVL_MIN) ||
(params->itvl_min > BLE_HCI_CONN_ITVL_MAX)) {
return BLE_ERR_INV_HCI_CMD_PARMS;
}
/* Check connection interval max */
if ((params->itvl_max < BLE_HCI_CONN_ITVL_MIN) ||
(params->itvl_max > BLE_HCI_CONN_ITVL_MAX) ||
(params->itvl_max < params->itvl_min)) {
return BLE_ERR_INV_HCI_CMD_PARMS;
}
/* Check connection latency */
if (params->latency > BLE_HCI_CONN_LATENCY_MAX) {
return BLE_ERR_INV_HCI_CMD_PARMS;
}
/* Check supervision timeout */
if ((params->supervision_timeout < BLE_HCI_CONN_SPVN_TIMEOUT_MIN) ||
(params->supervision_timeout > BLE_HCI_CONN_SPVN_TIMEOUT_MAX)) {
return BLE_ERR_INV_HCI_CMD_PARMS;
}
/* Check connection event length */
if (params->min_ce_len > params->max_ce_len) {
return BLE_ERR_INV_HCI_CMD_PARMS;
}
return 0;
}
/**
* @brief Converts a return code from the NimBLE stack to a text string.
* @param [in] rc The return code to convert.

View File

@@ -43,7 +43,6 @@ public:
static char* buildHexData(uint8_t* target, const uint8_t* source, uint8_t length);
static const char* advTypeToString(uint8_t advType);
static const char* returnCodeToString(int rc);
static int checkConnParams(ble_gap_conn_params* params);
};

View File

@@ -32,6 +32,18 @@
# endif
#endif
#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__)
# define NIMBLE_CPP_DEBUG_ASSERT(cond) \
if (!(cond)) { \
nimble_cpp_assert(NIMBLE_ATT_VAL_FILE, __LINE__); \
}
#else
# define NIMBLE_CPP_DEBUG_ASSERT(cond) (void(0))
#endif
#endif /* CONFIG_BT_ENABLED */
#ifdef _DOXYGEN_