mirror of
https://github.com/h2zero/esp-nimble-cpp.git
synced 2024-12-22 11:00:47 +01:00
Add NimBLEAttValue class. (#67)
This is a specialized container class to hold BLE attribute values. - Removes the use of std::string previously used to store the values. - Allows for setting/getting/notifying values from std::string, std::vector<uint8_t>, Arduino String, const char*, and uint8_t buffers. - Has operators retrieve the value as std::string, Arduino String, std::vector<uint8_t>, uint8_t* or char pointers. - Includes iterators and subscript/random access operator. - Introduces a max length parameter to the creation of server characteristics/descriptors to limit the size of the memory footprint. - Nearly Seamless integration with existing code. - Adds a config option to enable/disable timestamp storage when the value is updated. - Adds a config option to specify the initial size of the value container if not specified in the constructor.
This commit is contained in:
parent
be9cdc1ab6
commit
e3ee082dd7
17 changed files with 901 additions and 296 deletions
19
Kconfig
19
Kconfig
|
@ -49,5 +49,24 @@ config NIMBLE_CPP_ENABLE_ADVERTISMENT_TYPE_TEXT
|
|||
Enabling this option will display advertisment types recieved
|
||||
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
|
||||
bool "Enable timestamps to be stored with attribute values."
|
||||
default "n"
|
||||
help
|
||||
Enabling this option will store the timestamp when an attribute value is updated.
|
||||
This allows for checking the last update time using getTimeStamp()
|
||||
or getValue(time_t*). If disabled, the timestamp returned from these functions will be 0.
|
||||
Disabling timestamps will reduce the memory used for each value.
|
||||
|
||||
config NIMBLE_CPP_ATT_VALUE_INIT_LENGTH
|
||||
int "Initial attribute value size (bytes) for empty values."
|
||||
range 1 512
|
||||
default 20
|
||||
help
|
||||
Sets the default allocation size (bytes) for each attribute if not specified
|
||||
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.
|
||||
|
||||
endmenu
|
||||
|
|
|
@ -6,6 +6,24 @@ Sets the number of simultaneous connections (esp controller max is 9)
|
|||
- Default value is 3
|
||||
<br/>
|
||||
|
||||
`CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED`
|
||||
|
||||
Enable/disable storing the timestamp when an attribute value is updated
|
||||
This allows for checking the last update time using getTimeStamp() or getValue(time_t*)
|
||||
If disabled, the timestamp returned from these functions will be 0.
|
||||
Disabling timestamps will reduce the memory used for each value.
|
||||
1 = Enabled, 0 = Disabled; Default = Disabled
|
||||
<br/>
|
||||
|
||||
`CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH`
|
||||
|
||||
Set the default allocation size (bytes) for each attribute.
|
||||
If not specified 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.
|
||||
Default value is 20. Range: 1 : 512 (BLE_ATT_ATTR_MAX_LEN)
|
||||
<br/>
|
||||
|
||||
`CONFIG_BT_NIMBLE_ATT_PREFERRED_MTU`
|
||||
|
||||
Sets the default MTU size.
|
||||
|
|
447
src/NimBLEAttValue.h
Normal file
447
src/NimBLEAttValue.h
Normal file
|
@ -0,0 +1,447 @@
|
|||
/*
|
||||
* NimBLEAttValue.h
|
||||
*
|
||||
* Created: on March 18, 2021
|
||||
* Author H2zero
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef MAIN_NIMBLEATTVALUE_H_
|
||||
#define MAIN_NIMBLEATTVALUE_H_
|
||||
#include "nimconfig.h"
|
||||
#if defined(CONFIG_BT_ENABLED)
|
||||
|
||||
#ifdef NIMBLE_CPP_ARDUINO_STRING_AVAILABLE
|
||||
#include <Arduino.h>
|
||||
#endif
|
||||
|
||||
#include "NimBLELog.h"
|
||||
|
||||
/**** FIX COMPILATION ****/
|
||||
#undef min
|
||||
#undef max
|
||||
/**************************/
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#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
|
||||
|
||||
|
||||
/* 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 {};
|
||||
|
||||
|
||||
/**
|
||||
* @brief A specialized container class to hold BLE attribute values.
|
||||
* @details This class is designed to be more memory efficient than using\n
|
||||
* standard container types for value storage, while being convertable 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);
|
||||
|
||||
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);
|
||||
|
||||
/**
|
||||
* @brief Construct with an initial value from a buffer.
|
||||
* @param value A pointer to the initial value to set.
|
||||
* @param[in] len The size in bytes of the value to set.
|
||||
* @param[in] max_len The max size in bytes that the value can be.
|
||||
*/
|
||||
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.
|
||||
* @param[in] max_len The max size in bytes that the value can be.
|
||||
*/
|
||||
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 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){}
|
||||
|
||||
/**
|
||||
* @brief Construct with an initial value from a std::vector<uint8_t>.
|
||||
* @param vec A std::vector<uint8_t> containing to the initial value to set.
|
||||
* @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){}
|
||||
|
||||
#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
|
||||
|
||||
/** @brief Copy constructor */
|
||||
NimBLEAttValue(const NimBLEAttValue & source) { deepCopy(source); }
|
||||
|
||||
/** @brief Move constructor */
|
||||
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; }
|
||||
|
||||
/** @brief Returns the currently allocated capacity in bytes */
|
||||
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; }
|
||||
|
||||
/** @brief Returns the current size of the value in bytes */
|
||||
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; }
|
||||
|
||||
/** @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; }
|
||||
|
||||
/** @brief Iterator begin */
|
||||
const uint8_t* begin() const { return m_attr_value; }
|
||||
|
||||
/** @brief Iterator end */
|
||||
const uint8_t* end() const { return m_attr_value + m_attr_len; }
|
||||
|
||||
#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; }
|
||||
|
||||
/** @brief Set the timestamp to the current time */
|
||||
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
|
||||
|
||||
/**
|
||||
* @brief Set the value from a buffer
|
||||
* @param[in] value A ponter 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);
|
||||
|
||||
/**
|
||||
* @brief Set value to the value of const char*.
|
||||
* @param [in] s A ponter to a const char value to set.
|
||||
*/
|
||||
bool setValue(const char* s) {
|
||||
return setValue((uint8_t*)s, (uint16_t)strlen(s)); }
|
||||
|
||||
/**
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* @brief Append data to the value.
|
||||
* @param[in] value A ponter to a data buffer with the value to append.
|
||||
* @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);
|
||||
|
||||
|
||||
/*********************** Template Functions ************************/
|
||||
|
||||
/**
|
||||
* @brief Template to set value to the value of <type\>val.
|
||||
* @param [in] s The <type\>value to set.
|
||||
* @details Only used for types without a `c_str()` method.
|
||||
*/
|
||||
template<typename T>
|
||||
#ifdef _DOXYGEN_
|
||||
bool
|
||||
#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));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Template to set value to the value of <type\>val.
|
||||
* @param [in] s The <type\>value to set.
|
||||
* @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
|
||||
setValue(const T & s) {
|
||||
return setValue((uint8_t*)s.c_str(), (uint16_t)s.length());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Template to return the value as a <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\n
|
||||
* <tt>sizeof(<type\>)</tt>.
|
||||
* @return The data converted to <type\> or NULL if skipSizeCheck is false and the data is\n
|
||||
* less than <tt>sizeof(<type\>)</tt>.
|
||||
* @details <b>Use:</b> <tt>getValue<type>(×tamp, 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));
|
||||
}
|
||||
|
||||
|
||||
/*********************** Operators ************************/
|
||||
|
||||
/** @brief Subscript operator */
|
||||
uint8_t operator [](int pos) const {
|
||||
assert(pos < m_attr_len && "out of range"); 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); }
|
||||
|
||||
/** @brief Operator; Get the value as a std::string. */
|
||||
operator std::string() const {
|
||||
return std::string((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()); }
|
||||
|
||||
/** @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; }
|
||||
|
||||
/** @brief Move assignment operator */
|
||||
NimBLEAttValue& operator =(NimBLEAttValue && source);
|
||||
|
||||
/** @brief Copy assignment operator */
|
||||
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; }
|
||||
|
||||
/** @brief Inequality operator */
|
||||
bool operator !=(const NimBLEAttValue & source){ return !(*this == source); }
|
||||
|
||||
#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
|
||||
|
||||
};
|
||||
|
||||
|
||||
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_ */
|
|
@ -30,26 +30,29 @@ static const char* LOG_TAG = "NimBLECharacteristic";
|
|||
* @brief Construct a characteristic
|
||||
* @param [in] uuid - UUID (const char*) for the characteristic.
|
||||
* @param [in] properties - Properties for the characteristic.
|
||||
* @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, NimBLEService* pService)
|
||||
: NimBLECharacteristic(NimBLEUUID(uuid), properties, 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
|
||||
* @param [in] uuid - UUID for the characteristic.
|
||||
* @param [in] properties - Properties for the characteristic.
|
||||
* @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, NimBLEService* pService) {
|
||||
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_value = "";
|
||||
m_timestamp = 0;
|
||||
m_removed = 0;
|
||||
} // NimBLECharacteristic
|
||||
|
||||
|
@ -231,17 +234,14 @@ NimBLEUUID NimBLECharacteristic::getUUID() {
|
|||
|
||||
/**
|
||||
* @brief Retrieve the current value of the characteristic.
|
||||
* @return A std::string containing the current characteristic value.
|
||||
* @return The NimBLEAttValue containing the current characteristic value.
|
||||
*/
|
||||
std::string NimBLECharacteristic::getValue(time_t *timestamp) {
|
||||
ble_npl_hw_enter_critical();
|
||||
std::string retVal = m_value;
|
||||
NimBLEAttValue NimBLECharacteristic::getValue(time_t *timestamp) {
|
||||
if(timestamp != nullptr) {
|
||||
*timestamp = m_timestamp;
|
||||
m_value.getValue(timestamp);
|
||||
}
|
||||
ble_npl_hw_exit_critical(0);
|
||||
|
||||
return retVal;
|
||||
return m_value;
|
||||
} // getValue
|
||||
|
||||
|
||||
|
@ -250,10 +250,7 @@ std::string NimBLECharacteristic::getValue(time_t *timestamp) {
|
|||
* @return The length of the current characteristic data.
|
||||
*/
|
||||
size_t NimBLECharacteristic::getDataLength() {
|
||||
ble_npl_hw_enter_critical();
|
||||
size_t len = m_value.length();
|
||||
ble_npl_hw_exit_critical(0);
|
||||
return len;
|
||||
return m_value.size();
|
||||
}
|
||||
|
||||
|
||||
|
@ -286,25 +283,26 @@ int NimBLECharacteristic::handleGapEvent(uint16_t conn_handle, uint16_t attr_han
|
|||
}
|
||||
|
||||
ble_npl_hw_enter_critical();
|
||||
rc = os_mbuf_append(ctxt->om, (uint8_t*)pCharacteristic->m_value.data(),
|
||||
pCharacteristic->m_value.length());
|
||||
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: {
|
||||
if (ctxt->om->om_len > BLE_ATT_ATTR_MAX_LEN) {
|
||||
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[BLE_ATT_ATTR_MAX_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) > BLE_ATT_ATTR_MAX_LEN) {
|
||||
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);
|
||||
|
@ -384,36 +382,58 @@ void NimBLECharacteristic::setSubscribe(struct ble_gap_event *event) {
|
|||
|
||||
|
||||
/**
|
||||
* @brief Send an indication.\n
|
||||
* An indication is a transmission of up to the first 20 bytes of the characteristic value.\n
|
||||
* An indication will block waiting for a positive confirmation from the client.
|
||||
* @brief Send an indication.
|
||||
*/
|
||||
void NimBLECharacteristic::indicate() {
|
||||
NIMBLE_LOGD(LOG_TAG, ">> indicate: length: %d", getDataLength());
|
||||
notify(false);
|
||||
NIMBLE_LOGD(LOG_TAG, "<< indicate");
|
||||
} // indicate
|
||||
|
||||
|
||||
/**
|
||||
* @brief Send a notification.\n
|
||||
* A notification is a transmission of up to the first 20 bytes of the characteristic value.\n
|
||||
* A notification will not block; it is a fire and forget.
|
||||
* @brief Send an indication.
|
||||
* @param[in] value A pointer to the data to send.
|
||||
* @param[in] length The length of the data to send.
|
||||
*/
|
||||
void NimBLECharacteristic::indicate(const uint8_t* value, size_t length) {
|
||||
notify(value, length, false);
|
||||
} // indicate
|
||||
|
||||
|
||||
/**
|
||||
* @brief Send an indication.
|
||||
* @param[in] value A std::vector<uint8_t> containing the value to send as the notification value.
|
||||
*/
|
||||
void NimBLECharacteristic::indicate(const std::vector<uint8_t>& value) {
|
||||
notify(value.data(), value.size(), false);
|
||||
} // indicate
|
||||
|
||||
|
||||
/**
|
||||
* @brief Send a notification or indication.
|
||||
* @param[in] is_notification if true sends a notification, false sends an indication.
|
||||
*/
|
||||
void NimBLECharacteristic::notify(bool is_notification) {
|
||||
notify(getValue(), is_notification);
|
||||
}
|
||||
notify(m_value.data(), m_value.length(), is_notification);
|
||||
} // notify
|
||||
|
||||
|
||||
/**
|
||||
* @brief Send a notification.\n
|
||||
* A notification is a transmission of up to the first 20 bytes of the characteristic value.\n
|
||||
* A notification will not block; it is a fire and forget.
|
||||
* @param[in] value An optional value to send as the notification, else the current characteristic value is used.
|
||||
* @brief Send a notification or indication.
|
||||
* @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.
|
||||
*/
|
||||
void NimBLECharacteristic::notify(std::string value, bool is_notification) {
|
||||
size_t length = value.length();
|
||||
void NimBLECharacteristic::notify(const std::vector<uint8_t>& value, bool is_notification) {
|
||||
notify(value.data(), value.size(), is_notification);
|
||||
} // notify
|
||||
|
||||
|
||||
/**
|
||||
* @brief Send 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.
|
||||
*/
|
||||
void NimBLECharacteristic::notify(const uint8_t* value, size_t length, bool is_notification) {
|
||||
NIMBLE_LOGD(LOG_TAG, ">> notify: length: %d", length);
|
||||
|
||||
if(!(m_properties & NIMBLE_PROPERTY::NOTIFY) &&
|
||||
|
@ -472,7 +492,7 @@ void NimBLECharacteristic::notify(std::string value, bool is_notification) {
|
|||
// 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((uint8_t*)value.data(), length);
|
||||
os_mbuf *om = ble_hs_mbuf_from_flat(value, length);
|
||||
|
||||
if(!is_notification && (m_properties & NIMBLE_PROPERTY::INDICATE)) {
|
||||
if(!NimBLEDevice::getServer()->setIndicateWait(it.first)) {
|
||||
|
@ -516,40 +536,30 @@ NimBLECharacteristicCallbacks* NimBLECharacteristic::getCallbacks() {
|
|||
|
||||
|
||||
/**
|
||||
* @brief Set the value of the characteristic.
|
||||
* @param [in] data The data to set for the characteristic.
|
||||
* @param [in] length The length of the data in bytes.
|
||||
* @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());
|
||||
NIMBLE_LOGD(LOG_TAG, ">> setValue: length=%d, data=%s, characteristic UUID=%s",
|
||||
length, pHex, getUUID().toString().c_str());
|
||||
free(pHex);
|
||||
#endif
|
||||
|
||||
if (length > BLE_ATT_ATTR_MAX_LEN) {
|
||||
NIMBLE_LOGE(LOG_TAG, "Size %d too large, must be no bigger than %d", length, BLE_ATT_ATTR_MAX_LEN);
|
||||
return;
|
||||
}
|
||||
|
||||
time_t t = time(nullptr);
|
||||
ble_npl_hw_enter_critical();
|
||||
m_value = std::string((char*)data, length);
|
||||
m_timestamp = t;
|
||||
ble_npl_hw_exit_critical(0);
|
||||
|
||||
m_value.setValue(data, length);
|
||||
NIMBLE_LOGD(LOG_TAG, "<< setValue");
|
||||
} // setValue
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the value of the characteristic from string data.\n
|
||||
* We set the value of the characteristic from the bytes contained in the string.
|
||||
* @param [in] value the std::string value of the characteristic.
|
||||
* @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::string &value) {
|
||||
setValue((uint8_t*)(value.data()), value.length());
|
||||
} // setValue
|
||||
void NimBLECharacteristic::setValue(const std::vector<uint8_t>& vec) {
|
||||
return setValue((uint8_t*)&vec[0], vec.size());
|
||||
}// setValue
|
||||
|
||||
|
||||
/**
|
||||
|
|
|
@ -44,6 +44,7 @@ typedef enum {
|
|||
|
||||
#include "NimBLEService.h"
|
||||
#include "NimBLEDescriptor.h"
|
||||
#include "NimBLEAttValue.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
@ -65,11 +66,13 @@ public:
|
|||
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();
|
||||
|
@ -77,66 +80,93 @@ public:
|
|||
uint16_t getHandle();
|
||||
NimBLEUUID getUUID();
|
||||
std::string toString();
|
||||
|
||||
void setCallbacks(NimBLECharacteristicCallbacks* pCallbacks);
|
||||
NimBLECharacteristicCallbacks*
|
||||
getCallbacks();
|
||||
|
||||
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);
|
||||
void notify(std::string value, bool is_notification = true);
|
||||
|
||||
void notify(const uint8_t* value, size_t length, bool is_notification = true);
|
||||
void notify(const std::vector<uint8_t>& value, bool is_notification = true);
|
||||
size_t getSubscribedCount();
|
||||
|
||||
NimBLEDescriptor* createDescriptor(const char* uuid,
|
||||
uint32_t properties =
|
||||
NIMBLE_PROPERTY::READ |
|
||||
NIMBLE_PROPERTY::WRITE,
|
||||
uint16_t max_len = 100);
|
||||
NimBLEDescriptor* createDescriptor(const NimBLEUUID &uuid,
|
||||
uint32_t properties =
|
||||
NIMBLE_PROPERTY::READ |
|
||||
NIMBLE_PROPERTY::WRITE,
|
||||
uint16_t max_len = 100);
|
||||
|
||||
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);
|
||||
|
||||
std::string getValue(time_t *timestamp = nullptr);
|
||||
NimBLEService* getService();
|
||||
uint16_t getProperties();
|
||||
NimBLEAttValue getValue(time_t *timestamp = nullptr);
|
||||
size_t getDataLength();
|
||||
/**
|
||||
* @brief A template to convert the 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>(×tamp, skipSizeCheck);</tt>
|
||||
*/
|
||||
template<typename T>
|
||||
T getValue(time_t *timestamp = nullptr, bool skipSizeCheck = false) {
|
||||
std::string value = getValue();
|
||||
if(!skipSizeCheck && value.size() < sizeof(T)) return T();
|
||||
const char *pData = value.data();
|
||||
return *((T *)pData);
|
||||
}
|
||||
|
||||
void setValue(const uint8_t* data, size_t size);
|
||||
void setValue(const std::string &value);
|
||||
void setValue(const std::vector<uint8_t>& vec);
|
||||
void setCallbacks(NimBLECharacteristicCallbacks* pCallbacks);
|
||||
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();
|
||||
|
||||
|
||||
/*********************** Template Functions ************************/
|
||||
|
||||
/**
|
||||
* @brief Convenience template to set the characteristic value to <type\>val.
|
||||
* @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) {
|
||||
setValue((uint8_t*)&s, sizeof(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>(×tamp, skipSizeCheck);</tt>
|
||||
*/
|
||||
template<typename T>
|
||||
T getValue(time_t *timestamp = nullptr, bool skipSizeCheck = false) {
|
||||
return m_value.getValue<T>(timestamp, skipSizeCheck);
|
||||
}
|
||||
|
||||
NimBLEService* getService();
|
||||
uint16_t getProperties();
|
||||
/**
|
||||
* @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.
|
||||
* @param[in] value The <type\>value to set.
|
||||
* @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_
|
||||
void
|
||||
#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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Template to send an indication 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.
|
||||
* @param[in] value The <type\>value to set.
|
||||
* @details Only used if the <type\> has a `c_str()` method.
|
||||
*/
|
||||
template<typename T>
|
||||
#ifdef _DOXYGEN_
|
||||
void
|
||||
#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());
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
|
@ -153,9 +183,8 @@ private:
|
|||
uint16_t m_properties;
|
||||
NimBLECharacteristicCallbacks* m_pCallbacks;
|
||||
NimBLEService* m_pService;
|
||||
std::string m_value;
|
||||
NimBLEAttValue m_value;
|
||||
std::vector<NimBLEDescriptor*> m_dscVec;
|
||||
time_t m_timestamp;
|
||||
uint8_t m_removed;
|
||||
|
||||
std::vector<std::pair<uint16_t, uint16_t>> m_subscribedVec;
|
||||
|
@ -188,7 +217,7 @@ public:
|
|||
ERROR_INDICATE_FAILURE
|
||||
}Status;
|
||||
|
||||
virtual ~NimBLECharacteristicCallbacks();
|
||||
virtual ~NimBLECharacteristicCallbacks();
|
||||
virtual void onRead(NimBLECharacteristic* pCharacteristic);
|
||||
virtual void onRead(NimBLECharacteristic* pCharacteristic, ble_gap_conn_desc* desc);
|
||||
virtual void onWrite(NimBLECharacteristic* pCharacteristic);
|
||||
|
|
|
@ -768,11 +768,11 @@ int NimBLEClient::serviceDiscoveredCB(
|
|||
* @param [in] characteristicUUID The characteristic whose value we wish to read.
|
||||
* @returns characteristic value or an empty string if not found
|
||||
*/
|
||||
std::string NimBLEClient::getValue(const NimBLEUUID &serviceUUID, const NimBLEUUID &characteristicUUID) {
|
||||
NimBLEAttValue NimBLEClient::getValue(const NimBLEUUID &serviceUUID, const NimBLEUUID &characteristicUUID) {
|
||||
NIMBLE_LOGD(LOG_TAG, ">> getValue: serviceUUID: %s, characteristicUUID: %s",
|
||||
serviceUUID.toString().c_str(), characteristicUUID.toString().c_str());
|
||||
|
||||
std::string ret = "";
|
||||
NimBLEAttValue ret;
|
||||
NimBLERemoteService* pService = getService(serviceUUID);
|
||||
|
||||
if(pService != nullptr) {
|
||||
|
@ -796,7 +796,7 @@ std::string NimBLEClient::getValue(const NimBLEUUID &serviceUUID, const NimBLEUU
|
|||
* @returns true if successful otherwise false
|
||||
*/
|
||||
bool NimBLEClient::setValue(const NimBLEUUID &serviceUUID, const NimBLEUUID &characteristicUUID,
|
||||
const std::string &value, bool response)
|
||||
const NimBLEAttValue &value, bool response)
|
||||
{
|
||||
NIMBLE_LOGD(LOG_TAG, ">> setValue: serviceUUID: %s, characteristicUUID: %s",
|
||||
serviceUUID.toString().c_str(), characteristicUUID.toString().c_str());
|
||||
|
@ -857,7 +857,7 @@ uint16_t NimBLEClient::getMTU() {
|
|||
* @param [in] arg A pointer to the client instance that registered for this callback.
|
||||
*/
|
||||
/*STATIC*/
|
||||
int NimBLEClient::handleGapEvent(struct ble_gap_event *event, void *arg) {
|
||||
int NimBLEClient::handleGapEvent(struct ble_gap_event *event, void *arg) {
|
||||
NimBLEClient* client = (NimBLEClient*)arg;
|
||||
int rc;
|
||||
|
||||
|
@ -976,11 +976,7 @@ uint16_t NimBLEClient::getMTU() {
|
|||
(*characteristic)->toString().c_str());
|
||||
|
||||
uint32_t data_len = OS_MBUF_PKTLEN(event->notify_rx.om);
|
||||
time_t t = time(nullptr);
|
||||
ble_npl_hw_enter_critical();
|
||||
(*characteristic)->m_value = std::string((char *)event->notify_rx.om->om_data, data_len);
|
||||
(*characteristic)->m_timestamp = t;
|
||||
ble_npl_hw_exit_critical(0);
|
||||
(*characteristic)->m_value.setValue(event->notify_rx.om->om_data, data_len);
|
||||
|
||||
if ((*characteristic)->m_notifyCallback != nullptr) {
|
||||
NIMBLE_LOGD(LOG_TAG, "Invoking callback for notification on characteristic %s",
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include "NimBLEUUID.h"
|
||||
#include "NimBLEUtils.h"
|
||||
#include "NimBLEConnInfo.h"
|
||||
#include "NimBLEAttValue.h"
|
||||
#include "NimBLEAdvertisedDevice.h"
|
||||
#include "NimBLERemoteService.h"
|
||||
|
||||
|
@ -51,9 +52,9 @@ public:
|
|||
NimBLERemoteService* getService(const NimBLEUUID &uuid);
|
||||
void deleteServices();
|
||||
size_t deleteService(const NimBLEUUID &uuid);
|
||||
std::string getValue(const NimBLEUUID &serviceUUID, const NimBLEUUID &characteristicUUID);
|
||||
NimBLEAttValue getValue(const NimBLEUUID &serviceUUID, const NimBLEUUID &characteristicUUID);
|
||||
bool setValue(const NimBLEUUID &serviceUUID, const NimBLEUUID &characteristicUUID,
|
||||
const std::string &value, bool response = false);
|
||||
const NimBLEAttValue &value, bool response = false);
|
||||
NimBLERemoteCharacteristic* getCharacteristic(const uint16_t handle);
|
||||
bool isConnected();
|
||||
void setClientCallbacks(NimBLEClientCallbacks *pClientCallbacks,
|
||||
|
|
|
@ -28,27 +28,32 @@ static NimBLEDescriptorCallbacks defaultCallbacks;
|
|||
|
||||
|
||||
/**
|
||||
* @brief NimBLEDescriptor constructor.
|
||||
* @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), max_len, properties, pCharacteristic) {
|
||||
NimBLECharacteristic* pCharacteristic)
|
||||
: NimBLEDescriptor(NimBLEUUID(uuid), properties, max_len, pCharacteristic) {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief NimBLEDescriptor constructor.
|
||||
* @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_value.attr_len = 0; // Initial length is 0.
|
||||
m_value.attr_max_len = max_len; // Maximum length of the data.
|
||||
m_handle = NULL_HANDLE; // Handle is initially unknown.
|
||||
m_pCharacteristic = pCharacteristic;
|
||||
m_pCallbacks = &defaultCallbacks; // No initial callback.
|
||||
m_value.attr_value = (uint8_t*) calloc(max_len,1); // Allocate storage for the value.
|
||||
m_properties = 0;
|
||||
m_removed = 0;
|
||||
|
||||
|
@ -84,7 +89,6 @@ NimBLEDescriptor::NimBLEDescriptor(NimBLEUUID uuid, uint16_t properties, uint16_
|
|||
* @brief NimBLEDescriptor destructor.
|
||||
*/
|
||||
NimBLEDescriptor::~NimBLEDescriptor() {
|
||||
free(m_value.attr_value); // Release the storage we created in the constructor.
|
||||
} // ~NimBLEDescriptor
|
||||
|
||||
/**
|
||||
|
@ -101,7 +105,7 @@ uint16_t NimBLEDescriptor::getHandle() {
|
|||
* @return The length (in bytes) of the value of this descriptor.
|
||||
*/
|
||||
size_t NimBLEDescriptor::getLength() {
|
||||
return m_value.attr_len;
|
||||
return m_value.size();
|
||||
} // getLength
|
||||
|
||||
|
||||
|
@ -115,10 +119,14 @@ NimBLEUUID NimBLEDescriptor::getUUID() {
|
|||
|
||||
/**
|
||||
* @brief Get the value of this descriptor.
|
||||
* @return A pointer to the value of this descriptor.
|
||||
* @return The NimBLEAttValue of this descriptor.
|
||||
*/
|
||||
uint8_t* NimBLEDescriptor::getValue() {
|
||||
return m_value.attr_value;
|
||||
NimBLEAttValue NimBLEDescriptor::getValue(time_t *timestamp) {
|
||||
if (timestamp != nullptr) {
|
||||
m_value.getValue(timestamp);
|
||||
}
|
||||
|
||||
return m_value;
|
||||
} // getValue
|
||||
|
||||
|
||||
|
@ -127,7 +135,7 @@ uint8_t* NimBLEDescriptor::getValue() {
|
|||
* @return A std::string instance containing a copy of the descriptor's value.
|
||||
*/
|
||||
std::string NimBLEDescriptor::getStringValue() {
|
||||
return std::string((char *) m_value.attr_value, m_value.attr_len);
|
||||
return std::string(m_value);
|
||||
}
|
||||
|
||||
|
||||
|
@ -163,23 +171,25 @@ int NimBLEDescriptor::handleGapEvent(uint16_t conn_handle, uint16_t attr_handle,
|
|||
}
|
||||
|
||||
ble_npl_hw_enter_critical();
|
||||
rc = os_mbuf_append(ctxt->om, pDescriptor->getValue(), pDescriptor->getLength());
|
||||
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: {
|
||||
if (ctxt->om->om_len > pDescriptor->m_value.attr_max_len) {
|
||||
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[pDescriptor->m_value.attr_max_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) > pDescriptor->m_value.attr_max_len) {
|
||||
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);
|
||||
|
@ -231,27 +241,19 @@ void NimBLEDescriptor::setHandle(uint16_t handle) {
|
|||
* @param [in] length The length of the data in bytes.
|
||||
*/
|
||||
void NimBLEDescriptor::setValue(const uint8_t* data, size_t length) {
|
||||
if (length > m_value.attr_max_len) {
|
||||
NIMBLE_LOGE(LOG_TAG, "Size %d too large, must be no bigger than %d", length, m_value.attr_max_len);
|
||||
return;
|
||||
}
|
||||
|
||||
ble_npl_hw_enter_critical();
|
||||
m_value.attr_len = length;
|
||||
memcpy(m_value.attr_value, data, length);
|
||||
ble_npl_hw_exit_critical(0);
|
||||
|
||||
m_value.setValue(data, length);
|
||||
} // setValue
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the value of the descriptor.
|
||||
* @param [in] value The value of the descriptor in string form.
|
||||
* @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::string &value) {
|
||||
setValue((uint8_t*) value.data(), value.length());
|
||||
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 descriptior belongs to.
|
||||
|
|
|
@ -20,17 +20,10 @@
|
|||
|
||||
#include "NimBLECharacteristic.h"
|
||||
#include "NimBLEUUID.h"
|
||||
#include "NimBLEAttValue.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint16_t attr_max_len; /*!< attribute max value length */
|
||||
uint16_t attr_len; /*!< attribute current value length */
|
||||
uint8_t *attr_value; /*!< the pointer to attribute value */
|
||||
} attr_value_t;
|
||||
|
||||
class NimBLEService;
|
||||
class NimBLECharacteristic;
|
||||
class NimBLEDescriptorCallbacks;
|
||||
|
@ -54,24 +47,36 @@ public:
|
|||
uint16_t getHandle();
|
||||
NimBLEUUID getUUID();
|
||||
std::string toString();
|
||||
|
||||
void setCallbacks(NimBLEDescriptorCallbacks* pCallbacks);
|
||||
NimBLECharacteristic* getCharacteristic();
|
||||
|
||||
size_t getLength();
|
||||
uint8_t* getValue();
|
||||
NimBLEAttValue getValue(time_t *timestamp = nullptr);
|
||||
std::string getStringValue();
|
||||
|
||||
void setValue(const uint8_t* data, size_t size);
|
||||
void setValue(const std::string &value);
|
||||
NimBLECharacteristic* getCharacteristic();
|
||||
void setValue(const std::vector<uint8_t>& vec);
|
||||
|
||||
/*********************** Template Functions ************************/
|
||||
|
||||
/**
|
||||
* @brief Convenience template to set the descriptor value to <type\>val.
|
||||
* @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) {
|
||||
setValue((uint8_t*)&s, sizeof(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>(×tamp, skipSizeCheck);</tt>
|
||||
*/
|
||||
template<typename T>
|
||||
T getValue(time_t *timestamp = nullptr, bool skipSizeCheck = false) {
|
||||
return m_value.getValue<T>(timestamp, skipSizeCheck);
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -89,7 +94,7 @@ private:
|
|||
NimBLEDescriptorCallbacks* m_pCallbacks;
|
||||
NimBLECharacteristic* m_pCharacteristic;
|
||||
uint8_t m_properties;
|
||||
attr_value_t m_value;
|
||||
NimBLEAttValue m_value;
|
||||
uint8_t m_removed;
|
||||
}; // NimBLEDescriptor
|
||||
|
||||
|
|
|
@ -58,7 +58,6 @@ static const char* LOG_TAG = "NimBLERemoteCharacteristic";
|
|||
m_charProp = chr->properties;
|
||||
m_pRemoteService = pRemoteService;
|
||||
m_notifyCallback = nullptr;
|
||||
m_timestamp = 0;
|
||||
|
||||
NIMBLE_LOGD(LOG_TAG, "<< NimBLERemoteCharacteristic(): %s", m_uuid.toString().c_str());
|
||||
} // NimBLERemoteCharacteristic
|
||||
|
@ -414,15 +413,12 @@ NimBLEUUID NimBLERemoteCharacteristic::getUUID() {
|
|||
* @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.
|
||||
*/
|
||||
std::string NimBLERemoteCharacteristic::getValue(time_t *timestamp) {
|
||||
ble_npl_hw_enter_critical();
|
||||
std::string value = m_value;
|
||||
NimBLEAttValue NimBLERemoteCharacteristic::getValue(time_t *timestamp) {
|
||||
if(timestamp != nullptr) {
|
||||
*timestamp = m_timestamp;
|
||||
*timestamp = m_value.getTimeStamp();
|
||||
}
|
||||
ble_npl_hw_exit_critical(0);
|
||||
|
||||
return value;
|
||||
return m_value;
|
||||
}
|
||||
|
||||
|
||||
|
@ -470,12 +466,12 @@ float NimBLERemoteCharacteristic::readFloat() {
|
|||
* @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.
|
||||
*/
|
||||
std::string NimBLERemoteCharacteristic::readValue(time_t *timestamp) {
|
||||
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();
|
||||
std::string value;
|
||||
NimBLEAttValue value;
|
||||
|
||||
if (!pClient->isConnected()) {
|
||||
NIMBLE_LOGE(LOG_TAG, "Disconnected");
|
||||
|
@ -526,14 +522,11 @@ std::string NimBLERemoteCharacteristic::readValue(time_t *timestamp) {
|
|||
}
|
||||
} while(rc != 0 && retryCount--);
|
||||
|
||||
time_t t = time(nullptr);
|
||||
ble_npl_hw_enter_critical();
|
||||
value.setTimeStamp();
|
||||
m_value = value;
|
||||
m_timestamp = t;
|
||||
if(timestamp != nullptr) {
|
||||
*timestamp = m_timestamp;
|
||||
*timestamp = value.getTimeStamp();
|
||||
}
|
||||
ble_npl_hw_exit_critical(0);
|
||||
|
||||
NIMBLE_LOGD(LOG_TAG, "<< readValue length: %d rc=%d", value.length(), rc);
|
||||
return value;
|
||||
|
@ -558,17 +551,17 @@ int NimBLERemoteCharacteristic::onReadCB(uint16_t conn_handle,
|
|||
|
||||
NIMBLE_LOGI(LOG_TAG, "Read complete; status=%d conn_handle=%d", error->status, conn_handle);
|
||||
|
||||
std::string *strBuf = (std::string*)pTaskData->buf;
|
||||
NimBLEAttValue *valBuf = (NimBLEAttValue*)pTaskData->buf;
|
||||
int rc = error->status;
|
||||
|
||||
if(rc == 0) {
|
||||
if(attr) {
|
||||
uint16_t data_len = OS_MBUF_PKTLEN(attr->om);
|
||||
if(((*strBuf).length() + data_len) > BLE_ATT_ATTR_MAX_LEN) {
|
||||
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);
|
||||
(*strBuf) += std::string((char*) attr->om->om_data, data_len);
|
||||
valBuf->append(attr->om->om_data, data_len);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
@ -719,22 +712,33 @@ std::string NimBLERemoteCharacteristic::toString() {
|
|||
|
||||
|
||||
/**
|
||||
* @brief Write the new value for the characteristic.
|
||||
* @param [in] newValue The new value to write.
|
||||
* @param [in] response Do we expect a response?
|
||||
* @return false if not connected or cant perform write for some reason.
|
||||
* @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::string &newValue, bool response) {
|
||||
return writeValue((uint8_t*)newValue.c_str(), newValue.length(), response);
|
||||
bool NimBLERemoteCharacteristic::writeValue(const std::vector<uint8_t>& vec, bool response) {
|
||||
return writeValue((uint8_t*)&vec[0], vec.size(), response);
|
||||
} // writeValue
|
||||
|
||||
|
||||
/**
|
||||
* @brief Write the new value for the characteristic from a data buffer.
|
||||
* @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 cant perform write for some reason.
|
||||
* @return false if not connected or otherwise cannot perform write.
|
||||
*/
|
||||
bool NimBLERemoteCharacteristic::writeValue(const uint8_t* data, size_t length, bool response) {
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
#include "NimBLELog.h"
|
||||
|
||||
class NimBLERemoteService;
|
||||
class NimBLERemoteDescriptor;
|
||||
|
@ -60,47 +61,15 @@ public:
|
|||
uint16_t getHandle();
|
||||
uint16_t getDefHandle();
|
||||
NimBLEUUID getUUID();
|
||||
std::string readValue(time_t *timestamp = nullptr);
|
||||
|
||||
/**
|
||||
* @brief A 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>(×tamp, skipSizeCheck);</tt>
|
||||
*/
|
||||
template<typename T>
|
||||
T readValue(time_t *timestamp = nullptr, bool skipSizeCheck = false) {
|
||||
std::string value = readValue(timestamp);
|
||||
if(!skipSizeCheck && value.size() < sizeof(T)) return T();
|
||||
const char *pData = value.data();
|
||||
return *((T *)pData);
|
||||
}
|
||||
NimBLEAttValue readValue(time_t *timestamp = nullptr);
|
||||
std::string toString();
|
||||
NimBLERemoteService* getRemoteService();
|
||||
|
||||
uint8_t readUInt8() __attribute__ ((deprecated("Use template readValue<uint8_t>()")));
|
||||
uint16_t readUInt16() __attribute__ ((deprecated("Use template readValue<uint16_t>()")));
|
||||
uint32_t readUInt32() __attribute__ ((deprecated("Use template readValue<uint32_t>()")));
|
||||
float readFloat() __attribute__ ((deprecated("Use template readValue<float>()")));
|
||||
std::string getValue(time_t *timestamp = nullptr);
|
||||
|
||||
/**
|
||||
* @brief A 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>(×tamp, skipSizeCheck);</tt>
|
||||
*/
|
||||
template<typename T>
|
||||
T getValue(time_t *timestamp = nullptr, bool skipSizeCheck = false) {
|
||||
std::string value = getValue(timestamp);
|
||||
if(!skipSizeCheck && value.size() < sizeof(T)) return T();
|
||||
const char *pData = value.data();
|
||||
return *((T *)pData);
|
||||
}
|
||||
NimBLEAttValue getValue(time_t *timestamp = nullptr);
|
||||
|
||||
bool subscribe(bool notifications = true,
|
||||
notify_callback notifyCallback = nullptr,
|
||||
|
@ -113,20 +82,74 @@ public:
|
|||
bool writeValue(const uint8_t* data,
|
||||
size_t length,
|
||||
bool response = false);
|
||||
bool writeValue(const std::string &newValue,
|
||||
bool response = false);
|
||||
bool writeValue(const std::vector<uint8_t>& v, bool response = false);
|
||||
bool writeValue(const char* s, bool response = false);
|
||||
|
||||
|
||||
/*********************** Template Functions ************************/
|
||||
|
||||
/**
|
||||
* @brief Convenience template to set the remote characteristic value to <type\>val.
|
||||
* @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>
|
||||
bool writeValue(const T &s, bool response = false) {
|
||||
#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);
|
||||
}
|
||||
|
||||
std::string toString();
|
||||
NimBLERemoteService* getRemoteService();
|
||||
/**
|
||||
* @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>(×tamp, 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>(×tamp, 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:
|
||||
|
||||
|
@ -156,9 +179,8 @@ private:
|
|||
uint16_t m_defHandle;
|
||||
uint16_t m_endHandle;
|
||||
NimBLERemoteService* m_pRemoteService;
|
||||
std::string m_value;
|
||||
NimBLEAttValue m_value;
|
||||
notify_callback m_notifyCallback;
|
||||
time_t m_timestamp;
|
||||
|
||||
// We maintain a vector of descriptors owned by this characteristic.
|
||||
std::vector<NimBLERemoteDescriptor*> m_descriptorVector;
|
||||
|
|
|
@ -86,11 +86,7 @@ NimBLEUUID NimBLERemoteDescriptor::getUUID() {
|
|||
* @deprecated Use readValue<uint8_t>().
|
||||
*/
|
||||
uint8_t NimBLERemoteDescriptor::readUInt8() {
|
||||
std::string value = readValue();
|
||||
if (value.length() >= 1) {
|
||||
return (uint8_t) value[0];
|
||||
}
|
||||
return 0;
|
||||
return readValue<uint8_t>();
|
||||
} // readUInt8
|
||||
|
||||
|
||||
|
@ -100,11 +96,7 @@ uint8_t NimBLERemoteDescriptor::readUInt8() {
|
|||
* @deprecated Use readValue<uint16_t>().
|
||||
*/
|
||||
uint16_t NimBLERemoteDescriptor::readUInt16() {
|
||||
std::string value = readValue();
|
||||
if (value.length() >= 2) {
|
||||
return *(uint16_t*) value.data();
|
||||
}
|
||||
return 0;
|
||||
return readValue<uint16_t>();
|
||||
} // readUInt16
|
||||
|
||||
|
||||
|
@ -114,11 +106,7 @@ uint16_t NimBLERemoteDescriptor::readUInt16() {
|
|||
* @deprecated Use readValue<uint32_t>().
|
||||
*/
|
||||
uint32_t NimBLERemoteDescriptor::readUInt32() {
|
||||
std::string value = readValue();
|
||||
if (value.length() >= 4) {
|
||||
return *(uint32_t*) value.data();
|
||||
}
|
||||
return 0;
|
||||
return readValue<uint32_t>();
|
||||
} // readUInt32
|
||||
|
||||
|
||||
|
@ -126,11 +114,11 @@ uint32_t NimBLERemoteDescriptor::readUInt32() {
|
|||
* @brief Read the value of the remote descriptor.
|
||||
* @return The value of the remote descriptor.
|
||||
*/
|
||||
std::string NimBLERemoteDescriptor::readValue() {
|
||||
NimBLEAttValue NimBLERemoteDescriptor::readValue() {
|
||||
NIMBLE_LOGD(LOG_TAG, ">> Descriptor readValue: %s", toString().c_str());
|
||||
|
||||
NimBLEClient* pClient = getRemoteCharacteristic()->getRemoteService()->getClient();
|
||||
std::string value;
|
||||
NimBLEAttValue value;
|
||||
|
||||
if (!pClient->isConnected()) {
|
||||
NIMBLE_LOGE(LOG_TAG, "Disconnected");
|
||||
|
@ -204,17 +192,17 @@ int NimBLERemoteDescriptor::onReadCB(uint16_t conn_handle,
|
|||
|
||||
NIMBLE_LOGD(LOG_TAG, "Read complete; status=%d conn_handle=%d", error->status, conn_handle);
|
||||
|
||||
std::string *strBuf = (std::string*)pTaskData->buf;
|
||||
NimBLEAttValue *valBuf = (NimBLEAttValue*)pTaskData->buf;
|
||||
int rc = error->status;
|
||||
|
||||
if(rc == 0) {
|
||||
if(attr) {
|
||||
uint16_t data_len = OS_MBUF_PKTLEN(attr->om);
|
||||
if(((*strBuf).length() + data_len) > BLE_ATT_ATTR_MAX_LEN) {
|
||||
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);
|
||||
(*strBuf) += std::string((char*) attr->om->om_data, data_len);
|
||||
valBuf->append(attr->om->om_data, data_len);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
@ -267,11 +255,33 @@ int NimBLERemoteDescriptor::onWriteCB(uint16_t conn_handle,
|
|||
|
||||
|
||||
/**
|
||||
* @brief Write data to the BLE Remote Descriptor.
|
||||
* @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 True if successful
|
||||
* @return false if not connected or otherwise cannot perform write.
|
||||
*/
|
||||
bool NimBLERemoteDescriptor::writeValue(const uint8_t* data, size_t length, bool response) {
|
||||
|
||||
|
@ -352,14 +362,4 @@ bool NimBLERemoteDescriptor::writeValue(const uint8_t* data, size_t length, bool
|
|||
} // writeValue
|
||||
|
||||
|
||||
/**
|
||||
* @brief Write data represented as a string to the BLE Remote Descriptor.
|
||||
* @param [in] newValue The data to send to the remote descriptor.
|
||||
* @param [in] response True if we expect a response.
|
||||
* @return True if successful
|
||||
*/
|
||||
bool NimBLERemoteDescriptor::writeValue(const std::string &newValue, bool response) {
|
||||
return writeValue((uint8_t*) newValue.data(), newValue.length(), response);
|
||||
} // writeValue
|
||||
|
||||
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL */
|
||||
|
|
|
@ -29,10 +29,53 @@ public:
|
|||
uint16_t getHandle();
|
||||
NimBLERemoteCharacteristic* getRemoteCharacteristic();
|
||||
NimBLEUUID getUUID();
|
||||
std::string readValue();
|
||||
NimBLEAttValue readValue();
|
||||
|
||||
uint8_t readUInt8() __attribute__ ((deprecated("Use template readValue<uint8_t>()")));
|
||||
uint16_t readUInt16() __attribute__ ((deprecated("Use template readValue<uint16_t>()")));
|
||||
uint32_t readUInt32() __attribute__ ((deprecated("Use template readValue<uint32_t>()")));
|
||||
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);
|
||||
|
||||
|
||||
/*********************** Template Functions ************************/
|
||||
|
||||
/**
|
||||
* @brief A template to convert the remote descriptor data to <type\>.
|
||||
* @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
|
||||
|
@ -40,28 +83,10 @@ public:
|
|||
* @details <b>Use:</b> <tt>readValue<type>(skipSizeCheck);</tt>
|
||||
*/
|
||||
template<typename T>
|
||||
T readValue(bool skipSizeCheck = false) {
|
||||
std::string value = readValue();
|
||||
T readValue(bool skipSizeCheck = false) {
|
||||
NimBLEAttValue value = readValue();
|
||||
if(!skipSizeCheck && value.size() < sizeof(T)) return T();
|
||||
const char *pData = value.data();
|
||||
return *((T *)pData);
|
||||
}
|
||||
|
||||
uint8_t readUInt8() __attribute__ ((deprecated("Use template readValue<uint8_t>()")));
|
||||
uint16_t readUInt16() __attribute__ ((deprecated("Use template readValue<uint16_t>()")));
|
||||
uint32_t readUInt32() __attribute__ ((deprecated("Use template readValue<uint32_t>()")));
|
||||
std::string toString(void);
|
||||
bool writeValue(const uint8_t* data, size_t length, bool response = false);
|
||||
bool writeValue(const std::string &newValue, bool response = false);
|
||||
|
||||
/**
|
||||
* @brief Convenience template to set the remote descriptor value to <type\>val.
|
||||
* @param [in] s The value to write.
|
||||
* @param [in] response True == request write response.
|
||||
*/
|
||||
template<typename T>
|
||||
bool writeValue(const T &s, bool response = false) {
|
||||
return writeValue((uint8_t*)&s, sizeof(T), response);
|
||||
return *((T *)value.data());
|
||||
}
|
||||
|
||||
private:
|
||||
|
|
|
@ -250,10 +250,11 @@ uint16_t NimBLEService::getHandle() {
|
|||
* @brief Create a new BLE Characteristic associated with this service.
|
||||
* @param [in] uuid - The UUID of the characteristic.
|
||||
* @param [in] properties - The properties of the characteristic.
|
||||
* @param [in] max_len - The maximum length in bytes that the characteristic value can hold.
|
||||
* @return The new BLE characteristic.
|
||||
*/
|
||||
NimBLECharacteristic* NimBLEService::createCharacteristic(const char* uuid, uint32_t properties) {
|
||||
return createCharacteristic(NimBLEUUID(uuid), properties);
|
||||
NimBLECharacteristic* NimBLEService::createCharacteristic(const char* uuid, uint32_t properties, uint16_t max_len) {
|
||||
return createCharacteristic(NimBLEUUID(uuid), properties, max_len);
|
||||
}
|
||||
|
||||
|
||||
|
@ -261,10 +262,11 @@ NimBLECharacteristic* NimBLEService::createCharacteristic(const char* uuid, uint
|
|||
* @brief Create a new BLE Characteristic associated with this service.
|
||||
* @param [in] uuid - The UUID of the characteristic.
|
||||
* @param [in] properties - The properties of the characteristic.
|
||||
* @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) {
|
||||
NimBLECharacteristic* pCharacteristic = new NimBLECharacteristic(uuid, properties, this);
|
||||
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",
|
||||
|
|
|
@ -50,12 +50,14 @@ public:
|
|||
NimBLECharacteristic* createCharacteristic(const char* uuid,
|
||||
uint32_t properties =
|
||||
NIMBLE_PROPERTY::READ |
|
||||
NIMBLE_PROPERTY::WRITE);
|
||||
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);
|
||||
NIMBLE_PROPERTY::WRITE,
|
||||
uint16_t max_len = BLE_ATT_ATTR_MAX_LEN);
|
||||
|
||||
void addCharacteristic(NimBLECharacteristic* pCharacteristic);
|
||||
void removeCharacteristic(NimBLECharacteristic* pCharacteristic, bool deleteChr = false);
|
||||
|
|
|
@ -29,7 +29,7 @@ typedef struct {
|
|||
void *pATT;
|
||||
TaskHandle_t task;
|
||||
int rc;
|
||||
std::string *buf;
|
||||
void *buf;
|
||||
} ble_task_data_t;
|
||||
|
||||
|
||||
|
|
|
@ -25,6 +25,13 @@
|
|||
#define CONFIG_BT_NIMBLE_ROLE_BROADCASTER
|
||||
#endif
|
||||
|
||||
/* Enables the use of Arduino String class for attribute values */
|
||||
#if defined __has_include
|
||||
# if __has_include (<Arduino.h>)
|
||||
# define NIMBLE_CPP_ARDUINO_STRING_AVAILABLE
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#endif /* CONFIG_BT_ENABLED */
|
||||
|
||||
#ifdef _DOXYGEN_
|
||||
|
@ -32,6 +39,22 @@
|
|||
/** @brief Un-comment to change the number of simultaneous connections (esp controller max is 9) */
|
||||
#define CONFIG_BT_NIMBLE_MAX_CONNECTIONS 3
|
||||
|
||||
/** @brief Un-comment to enable storing the timestamp when an attribute value is updated\n
|
||||
* This allows for checking the last update time using getTimeStamp() or getValue(time_t*)\n
|
||||
* If disabled, the timestamp returned from these functions will be 0.\n
|
||||
* Disabling timestamps will reduce the memory used for each value.\n
|
||||
* 1 = Enabled, 0 = Disabled; Default = Disabled
|
||||
*/
|
||||
#define CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED 0
|
||||
|
||||
/** @brief Uncomment to set the default allocation size (bytes) for each attribute if\n
|
||||
* not specified when the constructor is called. This is also the size used when a remote\n
|
||||
* characteristic or descriptor is constructed before a value is read/notifed.\n
|
||||
* Increasing this will reduce reallocations but increase memory footprint.\n
|
||||
* Default value is 20. Range: 1 : 512 (BLE_ATT_ATTR_MAX_LEN)
|
||||
*/
|
||||
#define CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH 20
|
||||
|
||||
/** @brief Un-comment to change the default MTU size */
|
||||
#define CONFIG_BT_NIMBLE_ATT_PREFERRED_MTU 255
|
||||
|
||||
|
|
Loading…
Reference in a new issue