2020-03-29 17:44:20 -06:00
/*
* NimBLECharacteristic.cpp
*
* Created: on March 3, 2020
* Author H2zero
2020-05-13 22:03:56 -06:00
*
2020-03-29 17:44:20 -06:00
* BLECharacteristic.cpp
*
* Created on: Jun 22, 2017
* Author: kolban
*/
2020-05-13 22:03:56 -06:00
#include "nimconfig.h"
2021-09-06 21:14:43 -06:00
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
2020-05-13 22:03:56 -06:00
2020-03-29 17:44:20 -06:00
#include "NimBLECharacteristic.h"
#include "NimBLE2904.h"
#include "NimBLEDevice.h"
#include "NimBLELog.h"
#define NULL_HANDLE (0xffff)
2020-07-27 21:11:38 -06:00
#define NIMBLE_SUB_NOTIFY 0x0001
#define NIMBLE_SUB_INDICATE 0x0002
2020-03-29 17:44:20 -06:00
static NimBLECharacteristicCallbacks defaultCallback ;
static const char * LOG_TAG = "NimBLECharacteristic" ;
2020-07-27 21:11:38 -06:00
2020-03-29 17:44:20 -06:00
/**
* @brief Construct a characteristic
* @param [in] uuid - UUID (const char*) for the characteristic.
* @param [in] properties - Properties for the characteristic.
2022-01-16 20:11:18 -07:00
* @param [in] max_len - The maximum length in bytes that the characteristic value can hold. (Default: 512 bytes for esp32, 20 for all others).
2020-06-07 18:42:28 -06:00
* @param [in] pService - pointer to the service instance this characteristic belongs to.
2020-03-29 17:44:20 -06:00
*/
2022-01-16 20:11:18 -07:00
NimBLECharacteristic :: NimBLECharacteristic ( const char * uuid , uint16_t properties ,
uint16_t max_len , NimBLEService * pService )
: NimBLECharacteristic ( NimBLEUUID ( uuid ), properties , max_len , pService ) {
2020-03-29 17:44:20 -06:00
}
/**
* @brief Construct a characteristic
* @param [in] uuid - UUID for the characteristic.
* @param [in] properties - Properties for the characteristic.
2022-01-16 20:11:18 -07:00
* @param [in] max_len - The maximum length in bytes that the characteristic value can hold. (Default: 512 bytes for esp32, 20 for all others).
2020-06-07 18:42:28 -06:00
* @param [in] pService - pointer to the service instance this characteristic belongs to.
2020-03-29 17:44:20 -06:00
*/
2022-01-16 20:11:18 -07:00
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 ) {
2021-07-30 20:56:52 -06:00
m_uuid = uuid ;
m_handle = NULL_HANDLE ;
m_properties = properties ;
m_pCallbacks = & defaultCallback ;
m_pService = pService ;
m_removed = 0 ;
2020-03-29 17:44:20 -06:00
} // NimBLECharacteristic
/**
* @brief Destructor.
*/
NimBLECharacteristic ::~ NimBLECharacteristic () {
2020-07-13 21:24:07 -06:00
for ( auto & it : m_dscVec ) {
delete it ;
}
2020-06-07 18:42:28 -06:00
} // ~NimBLECharacteristic
2020-03-29 17:44:20 -06:00
/**
* @brief Create a new BLE Descriptor associated with this characteristic.
* @param [in] uuid - The UUID of the descriptor.
* @param [in] properties - The properties of the descriptor.
2020-07-08 19:27:26 -06:00
* @param [in] max_len - The max length in bytes of the descriptor value.
2020-03-29 17:44:20 -06:00
* @return The new BLE descriptor.
*/
NimBLEDescriptor * NimBLECharacteristic :: createDescriptor ( const char * uuid , uint32_t properties , uint16_t max_len ) {
2020-05-13 22:03:56 -06:00
return createDescriptor ( NimBLEUUID ( uuid ), properties , max_len );
2020-03-29 17:44:20 -06:00
}
/**
* @brief Create a new BLE Descriptor associated with this characteristic.
* @param [in] uuid - The UUID of the descriptor.
* @param [in] properties - The properties of the descriptor.
2020-07-08 19:27:26 -06:00
* @param [in] max_len - The max length in bytes of the descriptor value.
2020-03-29 17:44:20 -06:00
* @return The new BLE descriptor.
*/
2020-05-10 07:21:46 -06:00
NimBLEDescriptor * NimBLECharacteristic :: createDescriptor ( const NimBLEUUID & uuid , uint32_t properties , uint16_t max_len ) {
2020-05-13 22:03:56 -06:00
NimBLEDescriptor * pDescriptor = nullptr ;
2024-07-03 14:03:12 -06:00
if ( uuid == NimBLEUUID ( uint16_t ( 0x2904 ))) {
2020-05-13 22:03:56 -06:00
pDescriptor = new NimBLE2904 ( this );
} else {
pDescriptor = new NimBLEDescriptor ( uuid , properties , max_len , this );
}
2020-06-07 18:42:28 -06:00
2021-05-23 13:07:00 -06:00
addDescriptor ( pDescriptor );
2020-05-13 22:03:56 -06:00
return pDescriptor ;
2021-05-23 13:07:00 -06:00
} // createDescriptor
/**
* @brief Add a descriptor to the characteristic.
2021-07-30 20:56:52 -06:00
* @param [in] pDescriptor A pointer to the descriptor to add.
2021-05-23 13:07:00 -06:00
*/
void NimBLECharacteristic :: addDescriptor ( NimBLEDescriptor * pDescriptor ) {
2021-07-30 20:56:52 -06:00
bool foundRemoved = false ;
if ( pDescriptor -> m_removed > 0 ) {
for ( auto & it : m_dscVec ) {
if ( it == pDescriptor ) {
foundRemoved = true ;
pDescriptor -> m_removed = 0 ;
}
}
}
if ( ! foundRemoved ) {
m_dscVec . push_back ( pDescriptor );
}
2021-05-23 13:07:00 -06:00
pDescriptor -> setCharacteristic ( this );
2021-07-30 20:56:52 -06:00
NimBLEDevice :: getServer () -> serviceChanged ();
2021-05-23 13:07:00 -06:00
}
2020-03-29 17:44:20 -06:00
2021-07-30 20:56:52 -06:00
/**
2022-07-31 11:00:12 -06:00
* @brief Remove a descriptor from the characteristic.
* @param[in] pDescriptor A pointer to the descriptor instance to remove from the characteristic.
2021-07-30 20:56:52 -06:00
* @param[in] deleteDsc If true it will delete the descriptor instance and free it's resources.
*/
void NimBLECharacteristic :: removeDescriptor ( NimBLEDescriptor * pDescriptor , bool deleteDsc ) {
// Check if the descriptor was already removed and if so, check if this
// is being called to delete the object and do so if requested.
// Otherwise, ignore the call and return.
if ( pDescriptor -> m_removed > 0 ) {
if ( deleteDsc ) {
for ( auto it = m_dscVec . begin (); it != m_dscVec . end (); ++ it ) {
if (( * it ) == pDescriptor ) {
delete * it ;
m_dscVec . erase ( it );
break ;
}
}
}
return ;
}
pDescriptor -> m_removed = deleteDsc ? NIMBLE_ATT_REMOVE_DELETE : NIMBLE_ATT_REMOVE_HIDE ;
NimBLEDevice :: getServer () -> serviceChanged ();
} // removeDescriptor
2020-03-29 17:44:20 -06:00
/**
2021-02-08 15:28:32 +00:00
* @brief Return the BLE Descriptor for the given UUID.
* @param [in] uuid The UUID of the descriptor.
* @return A pointer to the descriptor object or nullptr if not found.
2020-03-29 17:44:20 -06:00
*/
2020-06-07 18:42:28 -06:00
NimBLEDescriptor * NimBLECharacteristic :: getDescriptorByUUID ( const char * uuid ) {
return getDescriptorByUUID ( NimBLEUUID ( uuid ));
2020-03-29 17:44:20 -06:00
} // getDescriptorByUUID
/**
2021-02-08 15:28:32 +00:00
* @brief Return the BLE Descriptor for the given UUID.
* @param [in] uuid The UUID of the descriptor.
* @return A pointer to the descriptor object or nullptr if not found.
2020-03-29 17:44:20 -06:00
*/
2020-06-07 18:42:28 -06:00
NimBLEDescriptor * NimBLECharacteristic :: getDescriptorByUUID ( const NimBLEUUID & uuid ) {
for ( auto & it : m_dscVec ) {
if ( it -> getUUID () == uuid ) {
return it ;
}
}
return nullptr ;
2020-03-29 17:44:20 -06:00
} // getDescriptorByUUID
2021-02-08 15:28:32 +00:00
/**
* @brief Return the BLE Descriptor for the given handle.
2021-02-08 11:46:11 -07:00
* @param [in] handle The handle of the descriptor.
2021-02-08 15:28:32 +00:00
* @return A pointer to the descriptor object or nullptr if not found.
*/
NimBLEDescriptor * NimBLECharacteristic :: getDescriptorByHandle ( uint16_t handle ) {
for ( auto & it : m_dscVec ) {
if ( it -> getHandle () == handle ) {
return it ;
}
}
return nullptr ;
}
2020-03-29 17:44:20 -06:00
/**
* @brief Get the handle of the characteristic.
* @return The handle of the characteristic.
*/
uint16_t NimBLECharacteristic :: getHandle () {
2020-05-13 22:03:56 -06:00
return m_handle ;
2020-03-29 17:44:20 -06:00
} // getHandle
2020-07-08 19:27:26 -06:00
/**
* @brief Get the properties of the characteristic.
* @return The properties of the characteristic.
*/
2020-06-21 20:26:16 -06:00
uint16_t NimBLECharacteristic :: getProperties () {
2020-05-13 22:03:56 -06:00
return m_properties ;
2020-03-29 17:44:20 -06:00
} // getProperties
/**
* @brief Get the service associated with this characteristic.
*/
NimBLEService * NimBLECharacteristic :: getService () {
2020-05-13 22:03:56 -06:00
return m_pService ;
2020-03-29 17:44:20 -06:00
} // getService
2021-05-23 13:07:00 -06:00
void NimBLECharacteristic :: setService ( NimBLEService * pService ) {
m_pService = pService ;
}
2020-03-29 17:44:20 -06:00
/**
* @brief Get the UUID of the characteristic.
* @return The UUID of the characteristic.
*/
NimBLEUUID NimBLECharacteristic :: getUUID () {
2020-05-13 22:03:56 -06:00
return m_uuid ;
2020-03-29 17:44:20 -06:00
} // getUUID
/**
* @brief Retrieve the current value of the characteristic.
2022-01-16 20:11:18 -07:00
* @return The NimBLEAttValue containing the current characteristic value.
2020-03-29 17:44:20 -06:00
*/
2022-01-16 20:11:18 -07:00
NimBLEAttValue NimBLECharacteristic :: getValue ( time_t * timestamp ) {
2020-07-01 17:32:41 -06:00
if ( timestamp != nullptr ) {
2022-01-16 20:11:18 -07:00
m_value . getValue ( timestamp );
2020-07-01 17:32:41 -06:00
}
2020-06-07 18:42:28 -06:00
2022-01-16 20:11:18 -07:00
return m_value ;
2020-03-29 17:44:20 -06:00
} // getValue
2020-04-23 15:17:09 -06:00
/**
* @brief Retrieve the the current data length of the characteristic.
* @return The length of the current characteristic data.
*/
2020-06-07 18:42:28 -06:00
size_t NimBLECharacteristic :: getDataLength () {
2022-01-16 20:11:18 -07:00
return m_value . size ();
2020-04-23 15:17:09 -06:00
}
2020-07-08 19:27:26 -06:00
/**
* @brief STATIC callback to handle events from the NimBLE stack.
*/
2020-03-29 17:44:20 -06:00
int NimBLECharacteristic :: handleGapEvent ( uint16_t conn_handle , uint16_t attr_handle ,
struct ble_gatt_access_ctxt * ctxt ,
void * arg )
{
2023-05-29 09:56:31 -04:00
if ( conn_handle > BLE_HCI_LE_CONN_HANDLE_MAX )
{
NIMBLE_LOGW ( LOG_TAG , "Conn_handle (%d) is above the maximum value (%d)" , conn_handle , BLE_HCI_LE_CONN_HANDLE_MAX );
return BLE_ATT_ERR_INVALID_HANDLE ;
}
2024-06-13 16:02:19 -06:00
2020-05-13 22:03:56 -06:00
const ble_uuid_t * uuid ;
2020-03-29 17:44:20 -06:00
int rc ;
2022-08-27 08:28:15 -06:00
NimBLEConnInfo peerInfo ;
2020-05-13 22:03:56 -06:00
NimBLECharacteristic * pCharacteristic = ( NimBLECharacteristic * ) arg ;
NIMBLE_LOGD ( LOG_TAG , "Characteristic %s %s event" , pCharacteristic -> getUUID (). toString (). c_str (),
2020-03-29 17:44:20 -06:00
ctxt -> op == BLE_GATT_ACCESS_OP_READ_CHR ? "Read" : "Write" );
2020-05-13 22:03:56 -06:00
uuid = ctxt -> chr -> uuid ;
2024-07-12 20:42:53 -06:00
if ( ble_uuid_cmp ( uuid , pCharacteristic -> getUUID (). getBase ()) == 0 ){
2020-03-29 17:44:20 -06:00
switch ( ctxt -> op ) {
case BLE_GATT_ACCESS_OP_READ_CHR : {
2024-06-13 16:02:19 -06:00
ble_gap_conn_find ( conn_handle , & peerInfo . m_desc );
2022-06-26 17:03:21 -06:00
// If the packet header is only 8 bytes this is a follow up of a long read
// so we don't want to call the onRead() callback again.
if ( ctxt -> om -> om_pkthdr_len > 8 ||
2024-07-03 13:01:57 -06:00
conn_handle == BLE_HS_CONN_HANDLE_NONE ||
2022-08-27 08:28:15 -06:00
pCharacteristic -> m_value . size () <= ( ble_att_mtu ( peerInfo . m_desc . conn_handle ) - 3 )) {
pCharacteristic -> m_pCallbacks -> onRead ( pCharacteristic , peerInfo );
2020-05-13 22:03:56 -06:00
}
2020-06-07 18:42:28 -06:00
2021-12-29 08:10:57 -07:00
ble_npl_hw_enter_critical ();
2022-01-16 20:11:18 -07:00
rc = os_mbuf_append ( ctxt -> om , pCharacteristic -> m_value . data (), pCharacteristic -> m_value . size ());
2021-12-29 08:10:57 -07:00
ble_npl_hw_exit_critical ( 0 );
2020-03-29 17:44:20 -06:00
return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES ;
}
2020-05-13 22:03:56 -06:00
2020-03-29 17:44:20 -06:00
case BLE_GATT_ACCESS_OP_WRITE_CHR : {
2022-01-16 20:11:18 -07:00
uint16_t att_max_len = pCharacteristic -> m_value . max_size ();
if ( ctxt -> om -> om_len > att_max_len ) {
2020-03-29 17:44:20 -06:00
return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN ;
}
2020-05-13 22:03:56 -06:00
2022-01-16 20:11:18 -07:00
uint8_t buf [ att_max_len ];
2020-06-07 18:42:28 -06:00
size_t len = ctxt -> om -> om_len ;
memcpy ( buf , ctxt -> om -> om_data , len );
2020-05-13 22:03:56 -06:00
os_mbuf * next ;
next = SLIST_NEXT ( ctxt -> om , om_next );
while ( next != NULL ){
2022-01-16 20:11:18 -07:00
if (( len + next -> om_len ) > att_max_len ) {
2020-06-07 18:42:28 -06:00
return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN ;
}
2020-08-20 09:24:04 +02:00
memcpy ( & buf [ len ], next -> om_data , next -> om_len );
2020-06-07 18:42:28 -06:00
len += next -> om_len ;
2020-05-13 22:03:56 -06:00
next = SLIST_NEXT ( next , om_next );
}
2024-06-13 16:02:19 -06:00
ble_gap_conn_find ( conn_handle , & peerInfo . m_desc );
2020-06-07 18:42:28 -06:00
pCharacteristic -> setValue ( buf , len );
2022-08-27 08:28:15 -06:00
pCharacteristic -> m_pCallbacks -> onWrite ( pCharacteristic , peerInfo );
2020-03-29 17:44:20 -06:00
return 0 ;
}
default :
break ;
}
2020-05-13 22:03:56 -06:00
}
2020-03-29 17:44:20 -06:00
return BLE_ATT_ERR_UNLIKELY ;
}
2020-05-13 22:03:56 -06:00
2020-03-29 17:44:20 -06:00
2020-07-27 21:11:38 -06:00
/**
* @brief Get the number of clients subscribed to the characteristic.
* @returns Number of clients subscribed to notifications / indications.
*/
size_t NimBLECharacteristic :: getSubscribedCount () {
return m_subscribedVec . size ();
}
2020-03-29 17:44:20 -06:00
/**
2020-07-08 19:27:26 -06:00
* @brief Set the subscribe status for this characteristic.\n
* This will maintain a vector of subscribed clients and their indicate/notify status.
2020-03-29 17:44:20 -06:00
*/
void NimBLECharacteristic :: setSubscribe ( struct ble_gap_event * event ) {
2022-08-27 08:28:15 -06:00
NimBLEConnInfo peerInfo ;
if ( ble_gap_conn_find ( event -> subscribe . conn_handle , & peerInfo . m_desc ) != 0 ) {
2020-03-29 17:44:20 -06:00
return ;
2020-05-13 22:03:56 -06:00
}
2020-07-27 21:11:38 -06:00
uint16_t subVal = 0 ;
if ( event -> subscribe . cur_notify > 0 && ( m_properties & NIMBLE_PROPERTY :: NOTIFY )) {
subVal |= NIMBLE_SUB_NOTIFY ;
}
if ( event -> subscribe . cur_indicate && ( m_properties & NIMBLE_PROPERTY :: INDICATE )) {
subVal |= NIMBLE_SUB_INDICATE ;
}
2020-05-13 22:03:56 -06:00
2020-07-27 21:11:38 -06:00
NIMBLE_LOGI ( LOG_TAG , "New subscribe value for conn: %d val: %d" ,
2021-05-17 14:08:02 -06:00
event -> subscribe . conn_handle , subVal );
if ( ! event -> subscribe . cur_indicate && event -> subscribe . prev_indicate ) {
NimBLEDevice :: getServer () -> clearIndicateWait ( event -> subscribe . conn_handle );
}
2020-05-13 22:03:56 -06:00
2020-07-27 21:11:38 -06:00
auto it = m_subscribedVec . begin ();
for (; it != m_subscribedVec . end (); ++ it ) {
if (( * it ). first == event -> subscribe . conn_handle ) {
2020-06-07 18:42:28 -06:00
break ;
}
2020-03-29 17:44:20 -06:00
}
2020-06-07 18:42:28 -06:00
if ( subVal > 0 ) {
2020-07-27 21:11:38 -06:00
if ( it == m_subscribedVec . end ()) {
m_subscribedVec . push_back ({ event -> subscribe . conn_handle , subVal });
2021-05-19 22:27:32 -06:00
} else {
( * it ). second = subVal ;
2020-06-07 18:42:28 -06:00
}
2020-07-27 21:11:38 -06:00
} else if ( it != m_subscribedVec . end ()) {
m_subscribedVec . erase ( it );
2020-03-29 17:44:20 -06:00
}
2021-05-19 22:27:32 -06:00
2022-08-27 08:28:15 -06:00
m_pCallbacks -> onSubscribe ( this , peerInfo , subVal );
2020-03-29 17:44:20 -06:00
}
/**
2022-01-16 20:11:18 -07:00
* @brief Send an indication.
2020-03-29 17:44:20 -06:00
*/
void NimBLECharacteristic :: indicate () {
2020-05-13 22:03:56 -06:00
notify ( false );
2020-03-29 17:44:20 -06:00
} // indicate
2020-05-13 22:03:56 -06:00
2021-12-29 03:11:37 +00:00
2020-03-29 17:44:20 -06:00
/**
2022-01-16 20:11:18 -07:00
* @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.
2020-07-08 19:27:26 -06:00
* @param[in] is_notification if true sends a notification, false sends an indication.
2022-09-12 05:18:55 +03:00
* @param[in] conn_handle Connection handle to send individual notification, or BLE_HCI_LE_CONN_HANDLE_MAX + 1 to send notification to all subscribed clients.
2020-03-29 17:44:20 -06:00
*/
2022-09-12 05:18:55 +03:00
void NimBLECharacteristic :: notify ( bool is_notification , uint16_t conn_handle ) {
notify ( m_value . data (), m_value . length (), is_notification , conn_handle );
2022-01-16 20:11:18 -07:00
} // notify
2020-03-29 17:44:20 -06:00
2021-12-29 03:11:37 +00:00
/**
2022-01-16 20:11:18 -07:00
* @brief Send a notification or indication.
* @param[in] value A std::vector<uint8_t> containing the value to send as the notification value.
2021-12-29 03:11:37 +00:00
* @param[in] is_notification if true sends a notification, false sends an indication.
2022-09-12 05:18:55 +03:00
* @param[in] conn_handle Connection handle to send individual notification, or BLE_HCI_LE_CONN_HANDLE_MAX + 1 to send notification to all subscribed clients.
2021-12-29 03:11:37 +00:00
*/
2022-09-12 05:18:55 +03:00
void NimBLECharacteristic :: notify ( const std :: vector < uint8_t >& value , bool is_notification , uint16_t conn_handle ) {
notify ( value . data (), value . size (), is_notification , conn_handle );
2022-01-16 20:11:18 -07:00
} // 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.
2022-09-12 05:18:55 +03:00
* @param[in] conn_handle Connection handle to send individual notification, or BLE_HCI_LE_CONN_HANDLE_MAX + 1 to send notification to all subscribed clients.
2022-01-16 20:11:18 -07:00
*/
2022-09-12 05:18:55 +03:00
void NimBLECharacteristic :: notify ( const uint8_t * value , size_t length , bool is_notification , uint16_t conn_handle ) {
2021-12-29 03:11:37 +00:00
NIMBLE_LOGD ( LOG_TAG , ">> notify: length: %d" , length );
2020-03-29 17:44:20 -06:00
2020-07-27 21:11:38 -06:00
if ( ! ( m_properties & NIMBLE_PROPERTY :: NOTIFY ) &&
! ( m_properties & NIMBLE_PROPERTY :: INDICATE ))
{
2020-06-07 18:42:28 -06:00
NIMBLE_LOGE ( LOG_TAG ,
2022-07-31 11:00:12 -06:00
"<< notify-Error; Notify/indicate not enabled for characteristic: %s" ,
2020-06-07 18:42:28 -06:00
std :: string ( getUUID ()). c_str ());
}
2020-03-29 17:44:20 -06:00
2020-07-27 21:11:38 -06:00
if ( m_subscribedVec . size () == 0 ) {
2020-06-07 18:42:28 -06:00
NIMBLE_LOGD ( LOG_TAG , "<< notify: No clients subscribed." );
2020-05-13 22:03:56 -06:00
return ;
}
2020-03-29 17:44:20 -06:00
m_pCallbacks -> onNotify ( this );
2021-12-29 08:10:57 -07:00
2020-06-21 20:26:16 -06:00
bool reqSec = ( m_properties & BLE_GATT_CHR_F_READ_AUTHEN ) ||
( m_properties & BLE_GATT_CHR_F_READ_AUTHOR ) ||
( m_properties & BLE_GATT_CHR_F_READ_ENC );
2020-03-29 17:44:20 -06:00
int rc = 0 ;
2020-05-13 22:03:56 -06:00
2020-07-27 21:11:38 -06:00
for ( auto & it : m_subscribedVec ) {
2022-09-12 05:18:55 +03:00
// check if need a specific client
if (( conn_handle <= BLE_HCI_LE_CONN_HANDLE_MAX ) && ( it . first != conn_handle )) {
continue ;
}
2021-12-29 08:08:25 -07:00
uint16_t _mtu = getService () -> getServer () -> getPeerMTU ( it . first ) - 3 ;
2020-05-13 22:03:56 -06:00
2020-06-11 08:06:16 -06:00
// check if connected and subscribed
2020-07-27 21:11:38 -06:00
if ( _mtu == 0 || it . second == 0 ) {
2020-06-07 18:42:28 -06:00
continue ;
}
2020-06-21 20:26:16 -06:00
// check if security requirements are satisfied
if ( reqSec ) {
struct ble_gap_conn_desc desc ;
2020-07-27 21:11:38 -06:00
rc = ble_gap_conn_find ( it . first , & desc );
2020-06-21 20:26:16 -06:00
if ( rc != 0 || ! desc . sec_state . encrypted ) {
continue ;
}
}
2021-12-29 08:08:25 -07:00
if ( length > _mtu ) {
NIMBLE_LOGW ( LOG_TAG , "- Truncating to %d bytes (maximum notify size)" , _mtu );
2020-05-13 22:03:56 -06:00
}
2020-07-27 21:11:38 -06:00
if ( is_notification && ( ! ( it . second & NIMBLE_SUB_NOTIFY ))) {
2020-05-13 22:03:56 -06:00
NIMBLE_LOGW ( LOG_TAG ,
2020-03-29 17:44:20 -06:00
"Sending notification to client subscribed to indications, sending indication instead" );
is_notification = false ;
}
2020-05-13 22:03:56 -06:00
2020-07-27 21:11:38 -06:00
if ( ! is_notification && ( ! ( it . second & NIMBLE_SUB_INDICATE ))) {
2020-03-29 17:44:20 -06:00
NIMBLE_LOGW ( LOG_TAG ,
2020-06-07 18:42:28 -06:00
"Sending indication to client subscribed to notification, sending notification instead" );
2020-03-29 17:44:20 -06:00
is_notification = true ;
}
2020-05-13 22:03:56 -06:00
2020-03-29 17:44:20 -06:00
// 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.
2022-01-16 20:11:18 -07:00
os_mbuf * om = ble_hs_mbuf_from_flat ( value , length );
2020-05-13 22:03:56 -06:00
2020-06-11 08:06:16 -06:00
if ( ! is_notification && ( m_properties & NIMBLE_PROPERTY :: INDICATE )) {
2021-05-17 14:08:02 -06:00
if ( ! NimBLEDevice :: getServer () -> setIndicateWait ( it . first )) {
NIMBLE_LOGE ( LOG_TAG , "prior Indication in progress" );
os_mbuf_free_chain ( om );
return ;
}
2020-06-11 08:06:16 -06:00
2020-07-27 21:11:38 -06:00
rc = ble_gattc_indicate_custom ( it . first , m_handle , om );
2020-03-29 17:44:20 -06:00
if ( rc != 0 ){
2021-05-17 14:08:02 -06:00
NimBLEDevice :: getServer () -> clearIndicateWait ( it . first );
2020-03-29 17:44:20 -06:00
}
} else {
2021-05-17 14:08:02 -06:00
ble_gattc_notify_custom ( it . first , m_handle , om );
2020-03-29 17:44:20 -06:00
}
2020-05-13 22:03:56 -06:00
}
2020-03-29 17:44:20 -06:00
2020-05-13 22:03:56 -06:00
NIMBLE_LOGD ( LOG_TAG , "<< notify" );
2020-03-29 17:44:20 -06:00
} // Notify
/**
* @brief Set the callback handlers for this characteristic.
2020-07-08 19:27:26 -06:00
* @param [in] pCallbacks An instance of a NimBLECharacteristicCallbacks class\n
* used to define any callbacks for the characteristic.
2020-03-29 17:44:20 -06:00
*/
void NimBLECharacteristic :: setCallbacks ( NimBLECharacteristicCallbacks * pCallbacks ) {
2020-05-13 22:03:56 -06:00
if ( pCallbacks != nullptr ){
m_pCallbacks = pCallbacks ;
} else {
m_pCallbacks = & defaultCallback ;
}
2020-03-29 17:44:20 -06:00
} // setCallbacks
2022-08-27 08:28:15 -06:00
2021-05-19 22:21:05 -06:00
/**
* @brief Get the callback handlers for this characteristic.
*/
NimBLECharacteristicCallbacks * NimBLECharacteristic :: getCallbacks () {
return m_pCallbacks ;
} //getCallbacks
2020-03-29 17:44:20 -06:00
/**
2022-01-16 20:11:18 -07:00
* @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.
2020-03-29 17:44:20 -06:00
*/
2020-05-10 07:21:46 -06:00
void NimBLECharacteristic :: setValue ( const uint8_t * data , size_t length ) {
2022-01-14 19:45:48 -07:00
#if CONFIG_NIMBLE_CPP_LOG_LEVEL >= 4
2020-05-13 22:03:56 -06:00
char * pHex = NimBLEUtils :: buildHexData ( nullptr , data , length );
2022-01-16 20:11:18 -07:00
NIMBLE_LOGD ( LOG_TAG , ">> setValue: length=%d, data=%s, characteristic UUID=%s" ,
length , pHex , getUUID (). toString (). c_str ());
2020-05-13 22:03:56 -06:00
free ( pHex );
2020-06-07 18:42:28 -06:00
#endif
2020-05-13 22:03:56 -06:00
2022-01-16 20:11:18 -07:00
m_value . setValue ( data , length );
2020-05-13 22:03:56 -06:00
NIMBLE_LOGD ( LOG_TAG , "<< setValue" );
2020-03-29 17:44:20 -06:00
} // setValue
/**
2022-01-16 20:11:18 -07:00
* @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.
2020-03-29 17:44:20 -06:00
*/
2022-01-16 20:11:18 -07:00
void NimBLECharacteristic :: setValue ( const std :: vector < uint8_t >& vec ) {
return setValue (( uint8_t * ) & vec [ 0 ], vec . size ());
} // setValue
2020-03-29 17:44:20 -06:00
/**
* @brief Return a string representation of the characteristic.
* @return A string representation of the characteristic.
*/
std :: string NimBLECharacteristic :: toString () {
2020-05-13 22:03:56 -06:00
std :: string res = "UUID: " + m_uuid . toString () + ", handle : 0x" ;
char hex [ 5 ];
snprintf ( hex , sizeof ( hex ), "%04x" , m_handle );
res += hex ;
res += " " ;
if ( m_properties & BLE_GATT_CHR_PROP_READ ) res += "Read " ;
if ( m_properties & BLE_GATT_CHR_PROP_WRITE ) res += "Write " ;
if ( m_properties & BLE_GATT_CHR_PROP_WRITE_NO_RSP ) res += "WriteNoResponse " ;
if ( m_properties & BLE_GATT_CHR_PROP_BROADCAST ) res += "Broadcast " ;
if ( m_properties & BLE_GATT_CHR_PROP_NOTIFY ) res += "Notify " ;
if ( m_properties & BLE_GATT_CHR_PROP_INDICATE ) res += "Indicate " ;
return res ;
2020-03-29 17:44:20 -06:00
} // toString
/**
* @brief Callback function to support a read request.
* @param [in] pCharacteristic The characteristic that is the source of the event.
2022-08-27 08:28:15 -06:00
* @param [in] connInfo A reference to a NimBLEConnInfo instance containing the peer info.
2020-03-29 17:44:20 -06:00
*/
2022-08-27 08:28:15 -06:00
void NimBLECharacteristicCallbacks :: onRead ( NimBLECharacteristic * pCharacteristic , NimBLEConnInfo & connInfo ) {
2020-05-13 22:03:56 -06:00
NIMBLE_LOGD ( "NimBLECharacteristicCallbacks" , "onRead: default" );
2020-03-29 17:44:20 -06:00
} // onRead
/**
* @brief Callback function to support a write request.
* @param [in] pCharacteristic The characteristic that is the source of the event.
2022-08-27 08:28:15 -06:00
* @param [in] connInfo A reference to a NimBLEConnInfo instance containing the peer info.
2020-03-29 17:44:20 -06:00
*/
2022-08-27 08:28:15 -06:00
void NimBLECharacteristicCallbacks :: onWrite ( NimBLECharacteristic * pCharacteristic , NimBLEConnInfo & connInfo ) {
2020-05-13 22:03:56 -06:00
NIMBLE_LOGD ( "NimBLECharacteristicCallbacks" , "onWrite: default" );
2020-03-29 17:44:20 -06:00
} // onWrite
/**
* @brief Callback function to support a Notify request.
* @param [in] pCharacteristic The characteristic that is the source of the event.
*/
void NimBLECharacteristicCallbacks :: onNotify ( NimBLECharacteristic * pCharacteristic ) {
2020-05-13 22:03:56 -06:00
NIMBLE_LOGD ( "NimBLECharacteristicCallbacks" , "onNotify: default" );
2020-03-29 17:44:20 -06:00
} // onNotify
/**
* @brief Callback function to support a Notify/Indicate Status report.
* @param [in] pCharacteristic The characteristic that is the source of the event.
2022-08-26 20:40:21 -06:00
* @param [in] code Status return code from the NimBLE stack.
* @details The status code for success is 0 for notifications and BLE_HS_EDONE for indications,
* any other value is an error.
2020-03-29 17:44:20 -06:00
*/
2022-08-26 20:40:21 -06:00
void NimBLECharacteristicCallbacks :: onStatus ( NimBLECharacteristic * pCharacteristic , int code ) {
2020-05-13 22:03:56 -06:00
NIMBLE_LOGD ( "NimBLECharacteristicCallbacks" , "onStatus: default" );
2020-03-29 17:44:20 -06:00
} // onStatus
2020-07-27 21:11:38 -06:00
/**
* @brief Callback function called when a client changes subscription status.
* @param [in] pCharacteristic The characteristic that is the source of the event.
2022-08-27 08:28:15 -06:00
* @param [in] connInfo A reference to a NimBLEConnInfo instance containing the peer info.
2020-07-27 21:11:38 -06:00
* @param [in] subValue The subscription status:
* * 0 = Un-Subscribed
* * 1 = Notifications
* * 2 = Indications
* * 3 = Notifications and Indications
*/
void NimBLECharacteristicCallbacks :: onSubscribe ( NimBLECharacteristic * pCharacteristic ,
2022-08-27 08:28:15 -06:00
NimBLEConnInfo & connInfo ,
2020-07-27 21:11:38 -06:00
uint16_t subValue )
{
NIMBLE_LOGD ( "NimBLECharacteristicCallbacks" , "onSubscribe: default" );
}
2021-09-06 21:14:43 -06:00
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL */