[Bugfix] notify/indicate incorrectly returning success with custom value

When sending an array of data with a length value the wrong overload/template was being
used and the length value was being interpreted as the connection handle.

This updates the template to disable it when an array is passed and will now also report the error.
This commit is contained in:
h2zero
2025-04-23 16:02:34 -06:00
committed by Ryan Powell
parent e18d78678f
commit e00dd88add
2 changed files with 41 additions and 21 deletions

View File

@@ -268,17 +268,32 @@ bool NimBLECharacteristic::sendValue(const uint8_t* value, size_t length, bool i
int rc = 0; int rc = 0;
if (value != nullptr && length > 0) { // custom notification value if (value != nullptr && length > 0) { // custom notification value
// Notify all connected peers unless a specific handle is provided os_mbuf* om = nullptr;
for (const auto& ch : NimBLEDevice::getServer()->getPeerDevices()) {
if (connHandle != BLE_HS_CONN_HANDLE_NONE && ch != connHandle) { if (connHandle != BLE_HS_CONN_HANDLE_NONE) { // only sending to specific peer
continue; // only send to the specific handle, minor inefficiency but saves code. om = ble_hs_mbuf_from_flat(value, length);
if (!om) {
rc = BLE_HS_ENOMEM;
goto done;
} }
// Null buffer will read the value from the characteristic
if (isNotification) {
rc = ble_gattc_notify_custom(connHandle, m_handle, om);
} else {
rc = ble_gattc_indicate_custom(connHandle, m_handle, om);
}
goto done;
}
// Notify all connected peers unless a specific handle is provided
for (const auto& ch : NimBLEDevice::getServer()->getPeerDevices()) {
// Must re-create the data buffer on each iteration because it is freed by the calls bellow. // Must re-create the data buffer on each iteration because it is freed by the calls bellow.
os_mbuf* om = ble_hs_mbuf_from_flat(value, length); om = ble_hs_mbuf_from_flat(value, length);
if (!om) { if (!om) {
NIMBLE_LOGE(LOG_TAG, "<< sendValue: failed to allocate mbuf"); rc = BLE_HS_ENOMEM;
return false; goto done;
} }
if (isNotification) { if (isNotification) {
@@ -286,24 +301,25 @@ bool NimBLECharacteristic::sendValue(const uint8_t* value, size_t length, bool i
} else { } else {
rc = ble_gattc_indicate_custom(ch, m_handle, om); rc = ble_gattc_indicate_custom(ch, m_handle, om);
} }
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "<< sendValue: failed to send value, rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc));
break;
}
} }
} else if (connHandle != BLE_HS_CONN_HANDLE_NONE) { // only sending to specific peer } else if (connHandle != BLE_HS_CONN_HANDLE_NONE) {
// Null buffer will read the value from the characteristic // Null buffer will read the value from the characteristic
if (isNotification) { if (isNotification) {
rc = ble_gattc_notify_custom(connHandle, m_handle, NULL); rc = ble_gattc_notify_custom(connHandle, m_handle, nullptr);
} else { } else {
rc = ble_gattc_indicate_custom(connHandle, m_handle, NULL); rc = ble_gattc_indicate_custom(connHandle, m_handle, nullptr);
} }
} else { // Notify or indicate to all connected peers the characteristic value } else { // Notify or indicate to all connected peers the characteristic value
ble_gatts_chr_updated(m_handle); ble_gatts_chr_updated(m_handle);
} }
return rc == 0; done:
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "failed to send value, rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc));
return false;
}
return true;
} // sendValue } // sendValue
void NimBLECharacteristic::readEvent(NimBLEConnInfo& connInfo) { void NimBLECharacteristic::readEvent(NimBLEConnInfo& connInfo) {

View File

@@ -87,7 +87,9 @@ class NimBLECharacteristic : public NimBLELocalValueAttribute {
# ifdef _DOXYGEN_ # ifdef _DOXYGEN_
bool bool
# else # else
typename std::enable_if<!std::is_pointer<T>::value && !Has_c_str_length<T>::value && !Has_data_size<T>::value, bool>::type typename std::enable_if<!std::is_pointer<T>::value && !std::is_array<T>::value && !Has_c_str_length<T>::value &&
!Has_data_size<T>::value,
bool>::type
# endif # endif
notify(const T& v, uint16_t connHandle = BLE_HS_CONN_HANDLE_NONE) const { notify(const T& v, uint16_t connHandle = BLE_HS_CONN_HANDLE_NONE) const {
return notify(reinterpret_cast<const uint8_t*>(&v), sizeof(T), connHandle); return notify(reinterpret_cast<const uint8_t*>(&v), sizeof(T), connHandle);
@@ -133,7 +135,9 @@ class NimBLECharacteristic : public NimBLELocalValueAttribute {
# ifdef _DOXYGEN_ # ifdef _DOXYGEN_
bool bool
# else # else
typename std::enable_if<!std::is_pointer<T>::value && !Has_c_str_length<T>::value && !Has_data_size<T>::value, bool>::type typename std::enable_if<!std::is_pointer<T>::value && !std::is_array<T>::value && !Has_c_str_length<T>::value &&
!Has_data_size<T>::value,
bool>::type
# endif # endif
indicate(const T& v, uint16_t connHandle = BLE_HS_CONN_HANDLE_NONE) const { indicate(const T& v, uint16_t connHandle = BLE_HS_CONN_HANDLE_NONE) const {
return indicate(reinterpret_cast<const uint8_t*>(&v), sizeof(T), connHandle); return indicate(reinterpret_cast<const uint8_t*>(&v), sizeof(T), connHandle);
@@ -182,8 +186,8 @@ class NimBLECharacteristic : public NimBLELocalValueAttribute {
* @note This function is only available if the type T is not a pointer. * @note This function is only available if the type T is not a pointer.
*/ */
template <typename T> template <typename T>
typename std::enable_if<!std::is_pointer<T>::value, bool>::type notify(const T& value, typename std::enable_if<!std::is_pointer<T>::value && !std::is_array<T>::value, bool>::type notify(
uint16_t connHandle = BLE_HS_CONN_HANDLE_NONE) const { const T& value, uint16_t connHandle = BLE_HS_CONN_HANDLE_NONE) const {
if constexpr (Has_data_size<T>::value) { if constexpr (Has_data_size<T>::value) {
return notify(reinterpret_cast<const uint8_t*>(value.data()), value.size(), connHandle); return notify(reinterpret_cast<const uint8_t*>(value.data()), value.size(), connHandle);
} else if constexpr (Has_c_str_length<T>::value) { } else if constexpr (Has_c_str_length<T>::value) {
@@ -204,7 +208,7 @@ class NimBLECharacteristic : public NimBLELocalValueAttribute {
* @note This function is only available if the type T is not a pointer. * @note This function is only available if the type T is not a pointer.
*/ */
template <typename T> template <typename T>
typename std::enable_if<!std::is_pointer<T>::value, bool>::type indicate( typename std::enable_if<!std::is_pointer<T>::value && !std::is_array<T>::value, bool>::type indicate(
const T& value, uint16_t connHandle = BLE_HS_CONN_HANDLE_NONE) const { const T& value, uint16_t connHandle = BLE_HS_CONN_HANDLE_NONE) const {
if constexpr (Has_data_size<T>::value) { if constexpr (Has_data_size<T>::value) {
return indicate(reinterpret_cast<const uint8_t*>(value.data()), value.size(), connHandle); return indicate(reinterpret_cast<const uint8_t*>(value.data()), value.size(), connHandle);