diff --git a/Kconfig b/Kconfig
index b2a655d..c13fde1 100644
--- a/Kconfig
+++ b/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
diff --git a/docs/Command_line_config.md b/docs/Command_line_config.md
index 796eb1c..3fe4acb 100644
--- a/docs/Command_line_config.md
+++ b/docs/Command_line_config.md
@@ -6,6 +6,24 @@ Sets the number of simultaneous connections (esp controller max is 9)
- Default value is 3
+`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
+
+
+`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)
+
+
`CONFIG_BT_NIMBLE_ATT_PREFERRED_MTU`
Sets the default MTU size.
diff --git a/src/NimBLEAttValue.h b/src/NimBLEAttValue.h
new file mode 100644
index 0000000..11cd3f8
--- /dev/null
+++ b/src/NimBLEAttValue.h
@@ -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
+#endif
+
+#include "NimBLELog.h"
+
+/**** FIX COMPILATION ****/
+#undef min
+#undef max
+/**************************/
+
+#include
+#include
+
+#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
+#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
+struct Has_c_str_len : std::false_type {};
+
+template
+struct Has_c_str_len().c_str())),
+ decltype(void(std::declval().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 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.
+ * @param vec A std::vector 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 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 val.
+ * @param [in] s The value to set.
+ * @details Only used for types without a `c_str()` method.
+ */
+ template
+#ifdef _DOXYGEN_
+ bool
+#else
+ typename std::enable_if::value, bool>::type
+#endif
+ setValue(const T &s) {
+ return setValue((uint8_t*)&s, sizeof(T));
+ }
+
+ /**
+ * @brief Template to set value to the value of val.
+ * @param [in] s The value to set.
+ * @details Only used if the has a `c_str()` method.
+ */
+ template
+#ifdef _DOXYGEN_
+ bool
+#else
+ typename std::enable_if::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 .
+ * @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
+ * sizeof().
+ * @return The data converted to or NULL if skipSizeCheck is false and the data is\n
+ * less than sizeof().
+ * @details Use: getValue(×tamp, skipSizeCheck);
+ */
+ template
+ 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. */
+ operator std::vector() const {
+ return std::vector(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_ */
diff --git a/src/NimBLECharacteristic.cpp b/src/NimBLECharacteristic.cpp
index cb447d6..3b95030 100644
--- a/src/NimBLECharacteristic.cpp
+++ b/src/NimBLECharacteristic.cpp
@@ -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 containing the value to send as the notification value.
+ */
+void NimBLECharacteristic::indicate(const std::vector& 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 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& 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`.\n
+ * @param [in] vec The std::vector 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& vec) {
+ return setValue((uint8_t*)&vec[0], vec.size());
+}// setValue
/**
diff --git a/src/NimBLECharacteristic.h b/src/NimBLECharacteristic.h
index 9af1120..0f84e2d 100644
--- a/src/NimBLECharacteristic.h
+++ b/src/NimBLECharacteristic.h
@@ -44,6 +44,7 @@ typedef enum {
#include "NimBLEService.h"
#include "NimBLEDescriptor.h"
+#include "NimBLEAttValue.h"
#include
#include
@@ -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& 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& 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 .
- * @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 sizeof().
- * @return The data converted to or NULL if skipSizeCheck is false and the data is
- * less than sizeof().
- * @details Use: getValue(×tamp, skipSizeCheck);
- */
- template
- 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& 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 val.
+ * @brief Template to set the characteristic value to val.
* @param [in] s The value to set.
*/
template
- void setValue(const T &s) {
- setValue((uint8_t*)&s, sizeof(T));
+ void setValue(const T &s) { m_value.setValue(s); }
+
+ /**
+ * @brief Template to convert the characteristic data to .
+ * @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 sizeof().
+ * @return The data converted to or NULL if skipSizeCheck is false and the data is less than sizeof().
+ * @details Use: getValue(×tamp, skipSizeCheck);
+ */
+ template
+ T getValue(time_t *timestamp = nullptr, bool skipSizeCheck = false) {
+ return m_value.getValue(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 value to set.
+ * @param[in] is_notification if true sends a notification, false sends an indication.
+ * @details Only used if the has a `c_str()` method.
+ */
+ template
+#ifdef _DOXYGEN_
+ void
+#else
+ typename std::enable_if::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 value to set.
+ * @details Only used if the has a `c_str()` method.
+ */
+ template
+#ifdef _DOXYGEN_
+ void
+#else
+ typename std::enable_if::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 m_dscVec;
- time_t m_timestamp;
uint8_t m_removed;
std::vector> 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);
diff --git a/src/NimBLEClient.cpp b/src/NimBLEClient.cpp
index 810159e..bc796cb 100644
--- a/src/NimBLEClient.cpp
+++ b/src/NimBLEClient.cpp
@@ -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",
diff --git a/src/NimBLEClient.h b/src/NimBLEClient.h
index 37700b7..7c93c30 100644
--- a/src/NimBLEClient.h
+++ b/src/NimBLEClient.h
@@ -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,
diff --git a/src/NimBLEDescriptor.cpp b/src/NimBLEDescriptor.cpp
index 5b0db1a..3429d13 100644
--- a/src/NimBLEDescriptor.cpp
+++ b/src/NimBLEDescriptor.cpp
@@ -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`.\n
+ * @param [in] vec The std::vector 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& 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.
diff --git a/src/NimBLEDescriptor.h b/src/NimBLEDescriptor.h
index fe6c733..4ee9a62 100644
--- a/src/NimBLEDescriptor.h
+++ b/src/NimBLEDescriptor.h
@@ -20,17 +20,10 @@
#include "NimBLECharacteristic.h"
#include "NimBLEUUID.h"
+#include "NimBLEAttValue.h"
#include
-
-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& vec);
+
+ /*********************** Template Functions ************************/
/**
- * @brief Convenience template to set the descriptor value to val.
+ * @brief Template to set the characteristic value to val.
* @param [in] s The value to set.
*/
template
- void setValue(const T &s) {
- setValue((uint8_t*)&s, sizeof(T));
+ void setValue(const T &s) { m_value.setValue(s); }
+
+ /**
+ * @brief Template to convert the descriptor data to .
+ * @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 sizeof().
+ * @return The data converted to or NULL if skipSizeCheck is false and the data is less than sizeof().
+ * @details Use: getValue(×tamp, skipSizeCheck);
+ */
+ template
+ T getValue(time_t *timestamp = nullptr, bool skipSizeCheck = false) {
+ return m_value.getValue(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
diff --git a/src/NimBLERemoteCharacteristic.cpp b/src/NimBLERemoteCharacteristic.cpp
index 8567967..3667742 100644
--- a/src/NimBLERemoteCharacteristic.cpp
+++ b/src/NimBLERemoteCharacteristic.cpp
@@ -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.
+ * @param [in] vec A std::vector 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& 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) {
diff --git a/src/NimBLERemoteCharacteristic.h b/src/NimBLERemoteCharacteristic.h
index 41ae816..353d832 100644
--- a/src/NimBLERemoteCharacteristic.h
+++ b/src/NimBLERemoteCharacteristic.h
@@ -23,6 +23,7 @@
#include
#include
+#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 .
- * @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 sizeof().
- * @return The data converted to or NULL if skipSizeCheck is false and the data is
- * less than sizeof().
- * @details Use: readValue(×tamp, skipSizeCheck);
- */
- template
- 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()")));
uint16_t readUInt16() __attribute__ ((deprecated("Use template readValue()")));
uint32_t readUInt32() __attribute__ ((deprecated("Use template readValue()")));
float readFloat() __attribute__ ((deprecated("Use template readValue()")));
- std::string getValue(time_t *timestamp = nullptr);
-
- /**
- * @brief A template to convert the remote characteristic data to .
- * @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 sizeof().
- * @return The data converted to or NULL if skipSizeCheck is false and the data is
- * less than sizeof().
- * @details Use: getValue(×tamp, skipSizeCheck);
- */
- template
- 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& v, bool response = false);
+ bool writeValue(const char* s, bool response = false);
+
+
+ /*********************** Template Functions ************************/
+
/**
- * @brief Convenience template to set the remote characteristic value to val.
+ * @brief Template to set the remote characteristic value to 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
- bool writeValue(const T &s, bool response = false) {
+#ifdef _DOXYGEN_
+ bool
+#else
+ typename std::enable_if::value && !Has_c_str_len::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 val.
+ * @param [in] s The value to write.
+ * @param [in] response True == request write response.
+ * @details Only used if the has a `c_str()` method.
+ */
+ template
+#ifdef _DOXYGEN_
+ bool
+#else
+ typename std::enable_if::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 .
+ * @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 sizeof().
+ * @return The data converted to or NULL if skipSizeCheck is false and the data is
+ * less than sizeof().
+ * @details Use: getValue(×tamp, skipSizeCheck);
+ */
+ template
+ 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 .
+ * @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 sizeof().
+ * @return The data converted to or NULL if skipSizeCheck is false and the data is
+ * less than sizeof().
+ * @details Use: readValue(×tamp, skipSizeCheck);
+ */
+ template
+ 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 m_descriptorVector;
diff --git a/src/NimBLERemoteDescriptor.cpp b/src/NimBLERemoteDescriptor.cpp
index 64ce06d..cae9103 100644
--- a/src/NimBLERemoteDescriptor.cpp
+++ b/src/NimBLERemoteDescriptor.cpp
@@ -86,11 +86,7 @@ NimBLEUUID NimBLERemoteDescriptor::getUUID() {
* @deprecated Use readValue().
*/
uint8_t NimBLERemoteDescriptor::readUInt8() {
- std::string value = readValue();
- if (value.length() >= 1) {
- return (uint8_t) value[0];
- }
- return 0;
+ return readValue();
} // readUInt8
@@ -100,11 +96,7 @@ uint8_t NimBLERemoteDescriptor::readUInt8() {
* @deprecated Use readValue().
*/
uint16_t NimBLERemoteDescriptor::readUInt16() {
- std::string value = readValue();
- if (value.length() >= 2) {
- return *(uint16_t*) value.data();
- }
- return 0;
+ return readValue();
} // readUInt16
@@ -114,11 +106,7 @@ uint16_t NimBLERemoteDescriptor::readUInt16() {
* @deprecated Use readValue().
*/
uint32_t NimBLERemoteDescriptor::readUInt32() {
- std::string value = readValue();
- if (value.length() >= 4) {
- return *(uint32_t*) value.data();
- }
- return 0;
+ return readValue();
} // 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.
+ * @param [in] vec A std::vector 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& 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 */
diff --git a/src/NimBLERemoteDescriptor.h b/src/NimBLERemoteDescriptor.h
index 13e8351..28863df 100644
--- a/src/NimBLERemoteDescriptor.h
+++ b/src/NimBLERemoteDescriptor.h
@@ -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()")));
+ uint16_t readUInt16() __attribute__ ((deprecated("Use template readValue()")));
+ uint32_t readUInt32() __attribute__ ((deprecated("Use template readValue()")));
+ std::string toString(void);
+ bool writeValue(const uint8_t* data, size_t length, bool response = false);
+ bool writeValue(const std::vector& v, bool response = false);
+ bool writeValue(const char* s, bool response = false);
+
+
+ /*********************** Template Functions ************************/
/**
- * @brief A template to convert the remote descriptor data to .
+ * @brief Template to set the remote descriptor value to 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
+#ifdef _DOXYGEN_
+ bool
+#else
+ typename std::enable_if::value && !Has_c_str_len::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 val.
+ * @param [in] s The value to write.
+ * @param [in] response True == request write response.
+ * @details Only used if the has a `c_str()` method.
+ */
+ template
+#ifdef _DOXYGEN_
+ bool
+#else
+ typename std::enable_if::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 .
* @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 sizeof().
* @return The data converted to or NULL if skipSizeCheck is false and the data is
@@ -40,28 +83,10 @@ public:
* @details Use: readValue(skipSizeCheck);
*/
template
- 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()")));
- uint16_t readUInt16() __attribute__ ((deprecated("Use template readValue()")));
- uint32_t readUInt32() __attribute__ ((deprecated("Use template readValue()")));
- 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 val.
- * @param [in] s The value to write.
- * @param [in] response True == request write response.
- */
- template
- bool writeValue(const T &s, bool response = false) {
- return writeValue((uint8_t*)&s, sizeof(T), response);
+ return *((T *)value.data());
}
private:
diff --git a/src/NimBLEService.cpp b/src/NimBLEService.cpp
index 18e15bc..a0f025b 100644
--- a/src/NimBLEService.cpp
+++ b/src/NimBLEService.cpp
@@ -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",
diff --git a/src/NimBLEService.h b/src/NimBLEService.h
index fbdd2e1..21ec1af 100644
--- a/src/NimBLEService.h
+++ b/src/NimBLEService.h
@@ -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);
diff --git a/src/NimBLEUtils.h b/src/NimBLEUtils.h
index 2fe4b3c..006d935 100644
--- a/src/NimBLEUtils.h
+++ b/src/NimBLEUtils.h
@@ -29,7 +29,7 @@ typedef struct {
void *pATT;
TaskHandle_t task;
int rc;
- std::string *buf;
+ void *buf;
} ble_task_data_t;
diff --git a/src/nimconfig.h b/src/nimconfig.h
index ea1840a..9c19031 100644
--- a/src/nimconfig.h
+++ b/src/nimconfig.h
@@ -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 ()
+# 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