[BREAKING] Refactor attributes

Refactor attributes to reduce code duplication and improve maintainability.

* Add attribute base classes to provide common code.
* Add const where possible to functions and parameters.
* `NimBLECharacteristic::notify` no longer takes a `bool is_notification` parameter, instead `indicate()` should be called to send indications.
* `NimBLECharacteristic::indicate` now takes the same parameters as `notify`.
* `NimBLECharacteristicCallbacks` and `NimBLEDescriptorCallbacks` methods now take `const NimBLEConnInfo&` instead of non-const.
* `NimBLECharacteristic::onNotify` callback removed as unnecessary, the library does not call notify without app input.
* `NimBLERemoteCharacteristic::getRemoteService` now returns a `const NimBLERemoteService*` instead of non-const.
* Add NimBLEUUID constructor that takes a reference to `ble_uuid_any_t`.
* `NimBLERemoteService::getCharacteristics` now returns a `const std::vector<NimBLERemoteCharacteristic*>&` instead of non-const `std::vector<NimBLERemoteCharacteristic*>*`
* `NimBLERemoteService::getValue` now returns `NimBLEAttValue` instead of `std::string`
* `NimBLEService::getCharacteristics` now returns a `const std::vector<NimBLECharacteristic*>&` instead of a copy of std::vector<NimBLECharacteristic *>.
* Remove const requirement for NimBLEConnInfo parameter in callbacks.
  Const is unnecessary as the data can't be changed by application code.
* Change NimBLERemoteCharacteristic::getRemoteService to return const pointer.
This commit is contained in:
h2zero
2024-07-26 14:47:36 -06:00
committed by h2zero
parent b4b3b0c455
commit 91210b8610
35 changed files with 1757 additions and 2536 deletions

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, NimBLECharacteristic* pCharacteristic)
: NimBLEDescriptor(NimBLEUUID(uuid), properties, max_len, pCharacteristic) {}
/**
* @brief Construct a descriptor
@@ -34,239 +41,64 @@ 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;
NimBLEDescriptor::NimBLEDescriptor(const NimBLEUUID& uuid, uint16_t properties, uint16_t max_len, 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.");
m_removed = 1;
} else {
m_removed = 0;
setRemoved(NIMBLE_ATT_REMOVE_HIDE);
}
if (properties & BLE_GATT_CHR_F_READ) { // convert uint16_t properties to uint8_t
m_properties |= BLE_ATT_F_READ;
// 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 & (BLE_GATT_CHR_F_WRITE_NO_RSP | BLE_GATT_CHR_F_WRITE)) {
m_properties |= BLE_ATT_F_WRITE;
if (properties & (NIMBLE_PROPERTY::WRITE_NR | NIMBLE_PROPERTY::WRITE)) {
descProperties |= BLE_ATT_F_WRITE;
}
if (properties & BLE_GATT_CHR_F_READ_ENC) {
m_properties |= BLE_ATT_F_READ_ENC;
if (properties & NIMBLE_PROPERTY::READ_ENC) {
descProperties |= BLE_ATT_F_READ_ENC;
}
if (properties & BLE_GATT_CHR_F_READ_AUTHEN) {
m_properties |= BLE_ATT_F_READ_AUTHEN;
if (properties & NIMBLE_PROPERTY::READ_AUTHEN) {
descProperties |= BLE_ATT_F_READ_AUTHEN;
}
if (properties & BLE_GATT_CHR_F_READ_AUTHOR) {
m_properties |= BLE_ATT_F_READ_AUTHOR;
if (properties & NIMBLE_PROPERTY::READ_AUTHOR) {
descProperties |= BLE_ATT_F_READ_AUTHOR;
}
if (properties & BLE_GATT_CHR_F_WRITE_ENC) {
m_properties |= BLE_ATT_F_WRITE_ENC;
if (properties & NIMBLE_PROPERTY::WRITE_ENC) {
descProperties |= BLE_ATT_F_WRITE_ENC;
}
if (properties & BLE_GATT_CHR_F_WRITE_AUTHEN) {
m_properties |= BLE_ATT_F_WRITE_AUTHEN;
if (properties & NIMBLE_PROPERTY::WRITE_AUTHEN) {
descProperties |= BLE_ATT_F_WRITE_AUTHEN;
}
if (properties & BLE_GATT_CHR_F_WRITE_AUTHOR) {
m_properties |= BLE_ATT_F_WRITE_AUTHOR;
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() {
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().getBase()) == 0){
switch(ctxt->op) {
case BLE_GATT_ACCESS_OP_READ_DSC: {
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 ||
conn_handle == BLE_HS_CONN_HANDLE_NONE ||
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: {
ble_gap_conn_find(conn_handle, &peerInfo.m_desc);
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.
@@ -275,18 +107,25 @@ void NimBLEDescriptor::setCharacteristic(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(NimBLEConnInfo& connInfo) {
m_pCallbacks->onRead(this, connInfo);
} // readEvent
void NimBLEDescriptor::writeEvent(const uint8_t* val, uint16_t len, NimBLEConnInfo& connInfo) {
setValue(val, len);
m_pCallbacks->onWrite(this, connInfo);
} // writeEvent
/**
* @brief Callback function to support a read request.
@@ -294,18 +133,15 @@ std::string NimBLEDescriptor::toString() {
* @param [in] connInfo A reference to a NimBLEConnInfo instance containing the peer info.
*/
void NimBLEDescriptorCallbacks::onRead(NimBLEDescriptor* pDescriptor, NimBLEConnInfo& connInfo) {
(void)pDescriptor;
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;
NIMBLE_LOGD("NimBLEDescriptorCallbacks", "onWrite: default");
} // onWrite