2020-03-30 01:44:20 +02:00
|
|
|
/*
|
|
|
|
* NimBLECharacteristic.cpp
|
|
|
|
*
|
|
|
|
* Created: on March 3, 2020
|
|
|
|
* Author H2zero
|
2020-05-14 06:03:56 +02:00
|
|
|
*
|
2020-03-30 01:44:20 +02:00
|
|
|
* BLECharacteristic.cpp
|
|
|
|
*
|
|
|
|
* Created on: Jun 22, 2017
|
|
|
|
* Author: kolban
|
|
|
|
*/
|
|
|
|
#include "sdkconfig.h"
|
|
|
|
#if defined(CONFIG_BT_ENABLED)
|
|
|
|
|
2020-05-14 06:03:56 +02:00
|
|
|
#include "nimconfig.h"
|
|
|
|
#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
|
|
|
|
|
2020-03-30 01:44:20 +02:00
|
|
|
#include "NimBLECharacteristic.h"
|
|
|
|
#include "NimBLE2902.h"
|
|
|
|
#include "NimBLE2904.h"
|
|
|
|
#include "NimBLEDevice.h"
|
|
|
|
#include "NimBLEUtils.h"
|
|
|
|
#include "NimBLELog.h"
|
|
|
|
|
|
|
|
#define NULL_HANDLE (0xffff)
|
|
|
|
|
|
|
|
static NimBLECharacteristicCallbacks defaultCallback;
|
|
|
|
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.
|
2020-06-08 02:42:28 +02:00
|
|
|
* @param [in] pService - pointer to the service instance this characteristic belongs to.
|
2020-03-30 01:44:20 +02:00
|
|
|
*/
|
2020-05-14 06:03:56 +02:00
|
|
|
NimBLECharacteristic::NimBLECharacteristic(const char* uuid, uint16_t properties, NimBLEService* pService)
|
2020-03-30 01:44:20 +02:00
|
|
|
: NimBLECharacteristic(NimBLEUUID(uuid), properties, pService) {
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Construct a characteristic
|
|
|
|
* @param [in] uuid - UUID for the characteristic.
|
|
|
|
* @param [in] properties - Properties for the characteristic.
|
2020-06-08 02:42:28 +02:00
|
|
|
* @param [in] pService - pointer to the service instance this characteristic belongs to.
|
2020-03-30 01:44:20 +02:00
|
|
|
*/
|
2020-05-10 15:21:46 +02:00
|
|
|
NimBLECharacteristic::NimBLECharacteristic(const NimBLEUUID &uuid, uint16_t properties, NimBLEService* pService) {
|
2020-05-14 06:03:56 +02:00
|
|
|
m_uuid = uuid;
|
|
|
|
m_handle = NULL_HANDLE;
|
|
|
|
m_properties = properties;
|
|
|
|
m_pCallbacks = &defaultCallback;
|
2020-03-30 01:44:20 +02:00
|
|
|
m_pService = pService;
|
2020-06-08 02:42:28 +02:00
|
|
|
m_value = "";
|
|
|
|
m_valMux = portMUX_INITIALIZER_UNLOCKED;
|
|
|
|
|
|
|
|
if(properties & NIMBLE_PROPERTY::INDICATE){
|
|
|
|
m_pIndSemaphore = new FreeRTOS::Semaphore("ConfEvt");
|
|
|
|
} else {
|
|
|
|
m_pIndSemaphore = nullptr;
|
|
|
|
}
|
2020-03-30 01:44:20 +02:00
|
|
|
} // NimBLECharacteristic
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Destructor.
|
|
|
|
*/
|
|
|
|
NimBLECharacteristic::~NimBLECharacteristic() {
|
2020-06-08 02:42:28 +02:00
|
|
|
if(m_pIndSemaphore != nullptr) {
|
|
|
|
delete(m_pIndSemaphore);
|
2020-05-14 06:03:56 +02:00
|
|
|
}
|
2020-06-08 02:42:28 +02:00
|
|
|
} // ~NimBLECharacteristic
|
2020-03-30 01:44:20 +02: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.
|
|
|
|
* @return The new BLE descriptor.
|
|
|
|
*/
|
|
|
|
NimBLEDescriptor* NimBLECharacteristic::createDescriptor(const char* uuid, uint32_t properties, uint16_t max_len) {
|
2020-05-14 06:03:56 +02:00
|
|
|
return createDescriptor(NimBLEUUID(uuid), properties, max_len);
|
2020-03-30 01:44:20 +02: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.
|
|
|
|
* @return The new BLE descriptor.
|
|
|
|
*/
|
2020-05-10 15:21:46 +02:00
|
|
|
NimBLEDescriptor* NimBLECharacteristic::createDescriptor(const NimBLEUUID &uuid, uint32_t properties, uint16_t max_len) {
|
2020-05-14 06:03:56 +02:00
|
|
|
NimBLEDescriptor* pDescriptor = nullptr;
|
2020-06-08 02:42:28 +02:00
|
|
|
if(uuid == NimBLEUUID(uint16_t(0x2902))) {
|
2020-04-14 03:13:51 +02:00
|
|
|
if(!(m_properties & BLE_GATT_CHR_F_NOTIFY) && !(m_properties & BLE_GATT_CHR_F_INDICATE)) {
|
|
|
|
assert(0 && "Cannot create 2902 descriptior without characteristic notification or indication property set");
|
|
|
|
}
|
|
|
|
// We cannot have more than one 2902 descriptor, if it's already been created just return a pointer to it.
|
2020-06-08 02:42:28 +02:00
|
|
|
pDescriptor = getDescriptorByUUID(uuid);
|
2020-04-14 03:13:51 +02:00
|
|
|
if(pDescriptor == nullptr) {
|
|
|
|
pDescriptor = new NimBLE2902(this);
|
|
|
|
} else {
|
|
|
|
return pDescriptor;
|
|
|
|
}
|
2020-05-14 06:03:56 +02:00
|
|
|
|
2020-06-08 02:42:28 +02:00
|
|
|
} else if (uuid == NimBLEUUID(uint16_t(0x2904))) {
|
2020-05-14 06:03:56 +02:00
|
|
|
pDescriptor = new NimBLE2904(this);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
pDescriptor = new NimBLEDescriptor(uuid, properties, max_len, this);
|
|
|
|
}
|
2020-06-08 02:42:28 +02:00
|
|
|
|
|
|
|
m_dscVec.push_back(pDescriptor);
|
2020-05-14 06:03:56 +02:00
|
|
|
return pDescriptor;
|
2020-03-30 01:44:20 +02:00
|
|
|
} // createCharacteristic
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Return the BLE Descriptor for the given UUID if associated with this characteristic.
|
|
|
|
* @param [in] descriptorUUID The UUID of the descriptor that we wish to retrieve.
|
|
|
|
* @return The BLE Descriptor. If no such descriptor is associated with the characteristic, nullptr is returned.
|
|
|
|
*/
|
2020-06-08 02:42:28 +02:00
|
|
|
NimBLEDescriptor* NimBLECharacteristic::getDescriptorByUUID(const char* uuid) {
|
|
|
|
return getDescriptorByUUID(NimBLEUUID(uuid));
|
2020-03-30 01:44:20 +02:00
|
|
|
} // getDescriptorByUUID
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Return the BLE Descriptor for the given UUID if associated with this characteristic.
|
|
|
|
* @param [in] descriptorUUID The UUID of the descriptor that we wish to retrieve.
|
|
|
|
* @return The BLE Descriptor. If no such descriptor is associated with the characteristic, nullptr is returned.
|
|
|
|
*/
|
2020-06-08 02:42:28 +02:00
|
|
|
NimBLEDescriptor* NimBLECharacteristic::getDescriptorByUUID(const NimBLEUUID &uuid) {
|
|
|
|
for (auto &it : m_dscVec) {
|
|
|
|
if (it->getUUID() == uuid) {
|
|
|
|
return it;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nullptr;
|
2020-03-30 01:44:20 +02:00
|
|
|
} // getDescriptorByUUID
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Get the handle of the characteristic.
|
|
|
|
* @return The handle of the characteristic.
|
|
|
|
*/
|
|
|
|
uint16_t NimBLECharacteristic::getHandle() {
|
2020-05-14 06:03:56 +02:00
|
|
|
return m_handle;
|
2020-03-30 01:44:20 +02:00
|
|
|
} // getHandle
|
|
|
|
|
|
|
|
|
|
|
|
uint8_t NimBLECharacteristic::getProperties() {
|
2020-05-14 06:03:56 +02:00
|
|
|
return m_properties;
|
2020-03-30 01:44:20 +02:00
|
|
|
} // getProperties
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Get the service associated with this characteristic.
|
|
|
|
*/
|
|
|
|
NimBLEService* NimBLECharacteristic::getService() {
|
2020-05-14 06:03:56 +02:00
|
|
|
return m_pService;
|
2020-03-30 01:44:20 +02:00
|
|
|
} // getService
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Get the UUID of the characteristic.
|
|
|
|
* @return The UUID of the characteristic.
|
|
|
|
*/
|
|
|
|
NimBLEUUID NimBLECharacteristic::getUUID() {
|
2020-05-14 06:03:56 +02:00
|
|
|
return m_uuid;
|
2020-03-30 01:44:20 +02:00
|
|
|
} // getUUID
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Retrieve the current value of the characteristic.
|
|
|
|
* @return A pointer to storage containing the current characteristic value.
|
|
|
|
*/
|
|
|
|
std::string NimBLECharacteristic::getValue() {
|
2020-06-08 02:42:28 +02:00
|
|
|
portENTER_CRITICAL(&m_valMux);
|
|
|
|
std::string retVal = m_value;
|
|
|
|
portEXIT_CRITICAL(&m_valMux);
|
2020-03-30 01:44:20 +02:00
|
|
|
|
2020-06-08 02:42:28 +02:00
|
|
|
return retVal;
|
|
|
|
} // getValue
|
2020-03-30 01:44:20 +02:00
|
|
|
|
|
|
|
|
2020-04-23 23:17:09 +02:00
|
|
|
/**
|
|
|
|
* @brief Retrieve the the current data length of the characteristic.
|
|
|
|
* @return The length of the current characteristic data.
|
|
|
|
*/
|
2020-06-08 02:42:28 +02:00
|
|
|
size_t NimBLECharacteristic::getDataLength() {
|
|
|
|
portENTER_CRITICAL(&m_valMux);
|
|
|
|
size_t len = m_value.length();
|
|
|
|
portEXIT_CRITICAL(&m_valMux);
|
|
|
|
|
|
|
|
return len;
|
2020-04-23 23:17:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-03-30 01:44:20 +02:00
|
|
|
int NimBLECharacteristic::handleGapEvent(uint16_t conn_handle, uint16_t attr_handle,
|
|
|
|
struct ble_gatt_access_ctxt *ctxt,
|
|
|
|
void *arg)
|
|
|
|
{
|
2020-05-14 06:03:56 +02:00
|
|
|
const ble_uuid_t *uuid;
|
2020-03-30 01:44:20 +02:00
|
|
|
int rc;
|
2020-05-14 06:03:56 +02:00
|
|
|
NimBLECharacteristic* pCharacteristic = (NimBLECharacteristic*)arg;
|
|
|
|
|
|
|
|
NIMBLE_LOGD(LOG_TAG, "Characteristic %s %s event", pCharacteristic->getUUID().toString().c_str(),
|
2020-03-30 01:44:20 +02:00
|
|
|
ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR ? "Read" : "Write");
|
2020-05-14 06:03:56 +02:00
|
|
|
|
|
|
|
uuid = ctxt->chr->uuid;
|
|
|
|
if(ble_uuid_cmp(uuid, &pCharacteristic->getUUID().getNative()->u) == 0){
|
2020-03-30 01:44:20 +02:00
|
|
|
switch(ctxt->op) {
|
|
|
|
case BLE_GATT_ACCESS_OP_READ_CHR: {
|
2020-05-14 06:03:56 +02: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) {
|
|
|
|
pCharacteristic->m_pCallbacks->onRead(pCharacteristic);
|
|
|
|
}
|
2020-06-08 02:42:28 +02:00
|
|
|
|
|
|
|
portENTER_CRITICAL(&pCharacteristic->m_valMux);
|
|
|
|
rc = os_mbuf_append(ctxt->om, (uint8_t*)pCharacteristic->m_value.data(),
|
|
|
|
pCharacteristic->m_value.length());
|
|
|
|
portEXIT_CRITICAL(&pCharacteristic->m_valMux);
|
|
|
|
|
2020-03-30 01:44:20 +02:00
|
|
|
return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
|
|
|
|
}
|
2020-05-14 06:03:56 +02:00
|
|
|
|
2020-03-30 01:44:20 +02:00
|
|
|
case BLE_GATT_ACCESS_OP_WRITE_CHR: {
|
|
|
|
if (ctxt->om->om_len > BLE_ATT_ATTR_MAX_LEN) {
|
|
|
|
return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
|
|
|
|
}
|
2020-05-14 06:03:56 +02:00
|
|
|
|
2020-06-08 02:42:28 +02:00
|
|
|
uint8_t buf[BLE_ATT_ATTR_MAX_LEN];
|
|
|
|
size_t len = ctxt->om->om_len;
|
|
|
|
memcpy(buf, ctxt->om->om_data,len);
|
|
|
|
|
2020-05-14 06:03:56 +02:00
|
|
|
os_mbuf *next;
|
|
|
|
next = SLIST_NEXT(ctxt->om, om_next);
|
|
|
|
while(next != NULL){
|
2020-06-08 02:42:28 +02:00
|
|
|
if((len + next->om_len) > BLE_ATT_ATTR_MAX_LEN) {
|
|
|
|
return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
|
|
|
|
}
|
|
|
|
memcpy(&buf[len-1], next->om_data, next->om_len);
|
|
|
|
len += next->om_len;
|
2020-05-14 06:03:56 +02:00
|
|
|
next = SLIST_NEXT(next, om_next);
|
|
|
|
}
|
2020-06-08 02:42:28 +02:00
|
|
|
|
|
|
|
pCharacteristic->setValue(buf, len);
|
2020-03-30 01:44:20 +02:00
|
|
|
pCharacteristic->m_pCallbacks->onWrite(pCharacteristic);
|
2020-04-14 03:13:51 +02:00
|
|
|
|
2020-03-30 01:44:20 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2020-05-14 06:03:56 +02:00
|
|
|
}
|
|
|
|
|
2020-03-30 01:44:20 +02:00
|
|
|
return BLE_ATT_ERR_UNLIKELY;
|
|
|
|
}
|
2020-05-14 06:03:56 +02:00
|
|
|
|
2020-03-30 01:44:20 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Set the subscribe status for this characteristic.
|
|
|
|
* This will maintain a map of subscribed clients and their indicate/notify status.
|
|
|
|
* @return N/A
|
|
|
|
*/
|
|
|
|
void NimBLECharacteristic::setSubscribe(struct ble_gap_event *event) {
|
|
|
|
uint16_t subVal = 0;
|
|
|
|
if(event->subscribe.cur_notify) {
|
|
|
|
subVal |= NIMBLE_DESC_FLAG_NOTIFY;
|
|
|
|
}
|
|
|
|
if(event->subscribe.cur_indicate) {
|
|
|
|
subVal |= NIMBLE_DESC_FLAG_INDICATE;
|
|
|
|
}
|
2020-05-14 06:03:56 +02:00
|
|
|
|
2020-06-08 02:42:28 +02:00
|
|
|
if(m_pIndSemaphore != nullptr) {
|
|
|
|
m_pIndSemaphore->give((subVal & NIMBLE_DESC_FLAG_INDICATE) ? 0 :
|
|
|
|
NimBLECharacteristicCallbacks::Status::ERROR_INDICATE_DISABLED);
|
|
|
|
}
|
2020-05-14 06:03:56 +02:00
|
|
|
|
2020-06-08 02:42:28 +02:00
|
|
|
NIMBLE_LOGI(LOG_TAG, "New subscribe value for conn: %d val: %d",
|
|
|
|
event->subscribe.conn_handle, subVal);
|
2020-05-14 06:03:56 +02:00
|
|
|
|
2020-06-08 02:42:28 +02:00
|
|
|
NimBLE2902* p2902 = (NimBLE2902*)getDescriptorByUUID(uint16_t(0x2902));
|
2020-05-14 06:03:56 +02:00
|
|
|
if(p2902 == nullptr){
|
2020-06-08 02:42:28 +02:00
|
|
|
ESP_LOGE(LOG_TAG, "No 2902 descriptor found for %s",
|
|
|
|
std::string(getUUID()).c_str());
|
2020-03-30 01:44:20 +02:00
|
|
|
return;
|
2020-05-14 06:03:56 +02:00
|
|
|
}
|
|
|
|
|
2020-03-30 01:44:20 +02:00
|
|
|
p2902->setNotifications(subVal & NIMBLE_DESC_FLAG_NOTIFY);
|
|
|
|
p2902->setIndications(subVal & NIMBLE_DESC_FLAG_INDICATE);
|
|
|
|
p2902->m_pCallbacks->onWrite(p2902);
|
2020-05-14 06:03:56 +02:00
|
|
|
|
|
|
|
|
2020-06-08 02:42:28 +02:00
|
|
|
auto it = p2902->m_subscribedVec.begin();
|
|
|
|
for(;it != p2902->m_subscribedVec.end(); ++it) {
|
|
|
|
if((*it).conn_id == event->subscribe.conn_handle) {
|
|
|
|
break;
|
|
|
|
}
|
2020-03-30 01:44:20 +02:00
|
|
|
}
|
2020-06-08 02:42:28 +02:00
|
|
|
|
|
|
|
if(subVal > 0) {
|
|
|
|
if(it == p2902->m_subscribedVec.end()) {
|
|
|
|
chr_sub_status_t client_sub;
|
|
|
|
client_sub.conn_id = event->subscribe.conn_handle;
|
|
|
|
client_sub.sub_val = subVal;
|
|
|
|
p2902->m_subscribedVec.push_back(client_sub);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
(*it).sub_val = subVal;
|
|
|
|
|
|
|
|
} else if(it != p2902->m_subscribedVec.end()) {
|
|
|
|
p2902->m_subscribedVec.erase(it);
|
|
|
|
p2902->m_subscribedVec.shrink_to_fit();
|
2020-03-30 01:44:20 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Send an indication.
|
|
|
|
* An indication is a transmission of up to the first 20 bytes of the characteristic value. An indication
|
|
|
|
* will block waiting a positive confirmation from the client.
|
|
|
|
* @return N/A
|
|
|
|
*/
|
|
|
|
void NimBLECharacteristic::indicate() {
|
2020-06-08 02:42:28 +02:00
|
|
|
NIMBLE_LOGD(LOG_TAG, ">> indicate: length: %d", getDataLength());
|
2020-05-14 06:03:56 +02:00
|
|
|
notify(false);
|
|
|
|
NIMBLE_LOGD(LOG_TAG, "<< indicate");
|
2020-03-30 01:44:20 +02:00
|
|
|
} // indicate
|
2020-05-14 06:03:56 +02:00
|
|
|
|
2020-03-30 01:44:20 +02:00
|
|
|
/**
|
|
|
|
* @brief Send a notify.
|
|
|
|
* A notification is a transmission of up to the first 20 bytes of the characteristic value. An notification
|
|
|
|
* will not block; it is a fire and forget.
|
|
|
|
* @return N/A.
|
|
|
|
*/
|
|
|
|
void NimBLECharacteristic::notify(bool is_notification) {
|
2020-06-08 02:42:28 +02:00
|
|
|
NIMBLE_LOGD(LOG_TAG, ">> notify: length: %d", getDataLength());
|
2020-03-30 01:44:20 +02:00
|
|
|
|
2020-06-08 02:42:28 +02:00
|
|
|
NimBLE2902* p2902 = (NimBLE2902*)getDescriptorByUUID(uint16_t(0x2902));
|
2020-03-30 01:44:20 +02:00
|
|
|
|
2020-06-08 02:42:28 +02:00
|
|
|
if(p2902 == nullptr) {
|
|
|
|
NIMBLE_LOGE(LOG_TAG,
|
|
|
|
"<< notify-Error; Notify/indicate not enabled for characterisitc: %s",
|
|
|
|
std::string(getUUID()).c_str());
|
|
|
|
}
|
2020-03-30 01:44:20 +02:00
|
|
|
|
2020-06-08 02:42:28 +02:00
|
|
|
if (p2902->m_subscribedVec.size() == 0) {
|
|
|
|
NIMBLE_LOGD(LOG_TAG, "<< notify: No clients subscribed.");
|
2020-05-14 06:03:56 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-03-30 01:44:20 +02:00
|
|
|
m_pCallbacks->onNotify(this);
|
2020-05-14 06:03:56 +02:00
|
|
|
|
2020-06-08 02:42:28 +02:00
|
|
|
std::string value = getValue();
|
|
|
|
size_t length = value.length();
|
2020-03-30 01:44:20 +02:00
|
|
|
int rc = 0;
|
2020-05-14 06:03:56 +02:00
|
|
|
|
2020-06-08 02:42:28 +02:00
|
|
|
for (auto &it : p2902->m_subscribedVec) {
|
|
|
|
uint16_t _mtu = getService()->getServer()->getPeerMTU(it.conn_id);
|
2020-03-30 01:44:20 +02:00
|
|
|
os_mbuf *om;
|
2020-05-14 06:03:56 +02:00
|
|
|
|
2020-03-30 01:44:20 +02:00
|
|
|
if(_mtu == 0) {
|
2020-06-08 02:42:28 +02:00
|
|
|
//NIMBLE_LOGD(LOG_TAG, "peer not connected");
|
2020-03-30 01:44:20 +02:00
|
|
|
continue;
|
|
|
|
}
|
2020-05-14 06:03:56 +02:00
|
|
|
|
2020-06-08 02:42:28 +02:00
|
|
|
if(it.sub_val == 0) {
|
|
|
|
//NIMBLE_LOGD(LOG_TAG, "Skipping unsubscribed client");
|
|
|
|
continue;
|
2020-05-14 06:03:56 +02:00
|
|
|
}
|
|
|
|
|
2020-06-08 02:42:28 +02:00
|
|
|
if (length > _mtu - 3) {
|
|
|
|
NIMBLE_LOGW(LOG_TAG, "- Truncating to %d bytes (maximum notify size)", _mtu - 3);
|
2020-03-30 01:44:20 +02:00
|
|
|
}
|
2020-05-14 06:03:56 +02:00
|
|
|
|
2020-06-08 02:42:28 +02:00
|
|
|
if(is_notification && (!(it.sub_val & NIMBLE_DESC_FLAG_NOTIFY))) {
|
2020-05-14 06:03:56 +02:00
|
|
|
NIMBLE_LOGW(LOG_TAG,
|
2020-03-30 01:44:20 +02:00
|
|
|
"Sending notification to client subscribed to indications, sending indication instead");
|
|
|
|
is_notification = false;
|
|
|
|
}
|
2020-05-14 06:03:56 +02:00
|
|
|
|
2020-06-08 02:42:28 +02:00
|
|
|
if(!is_notification && (!(it.sub_val & NIMBLE_DESC_FLAG_INDICATE))) {
|
2020-03-30 01:44:20 +02:00
|
|
|
NIMBLE_LOGW(LOG_TAG,
|
2020-06-08 02:42:28 +02:00
|
|
|
"Sending indication to client subscribed to notification, sending notification instead");
|
2020-03-30 01:44:20 +02:00
|
|
|
is_notification = true;
|
|
|
|
}
|
2020-05-14 06:03:56 +02:00
|
|
|
|
2020-03-30 01:44:20 +02: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.
|
2020-06-08 02:42:28 +02:00
|
|
|
om = ble_hs_mbuf_from_flat((uint8_t*)value.data(), length);
|
2020-05-14 06:03:56 +02:00
|
|
|
|
2020-06-08 02:42:28 +02:00
|
|
|
NimBLECharacteristicCallbacks::Status statusRC;
|
|
|
|
if(m_pIndSemaphore != nullptr && !is_notification) {
|
|
|
|
m_pIndSemaphore->take("indicate");
|
|
|
|
rc = ble_gattc_indicate_custom(it.conn_id, m_handle, om);
|
2020-03-30 01:44:20 +02:00
|
|
|
if(rc != 0){
|
2020-06-08 02:42:28 +02:00
|
|
|
m_pIndSemaphore->give();
|
|
|
|
statusRC = NimBLECharacteristicCallbacks::Status::ERROR_GATT;
|
|
|
|
} else {
|
|
|
|
rc = m_pIndSemaphore->wait();
|
2020-03-30 01:44:20 +02:00
|
|
|
}
|
2020-05-14 06:03:56 +02:00
|
|
|
|
2020-06-08 02:42:28 +02:00
|
|
|
if(rc == 0 || rc == BLE_HS_EDONE) {
|
|
|
|
rc = 0;
|
|
|
|
statusRC = NimBLECharacteristicCallbacks::Status::SUCCESS_INDICATE;
|
|
|
|
} else if(rc == BLE_HS_ETIMEOUT) {
|
|
|
|
statusRC = NimBLECharacteristicCallbacks::Status::ERROR_INDICATE_TIMEOUT;
|
2020-03-30 01:44:20 +02:00
|
|
|
} else {
|
2020-06-08 02:42:28 +02:00
|
|
|
statusRC = NimBLECharacteristicCallbacks::Status::ERROR_INDICATE_FAILURE;
|
2020-03-30 01:44:20 +02:00
|
|
|
}
|
|
|
|
} else {
|
2020-06-08 02:42:28 +02:00
|
|
|
rc = ble_gattc_notify_custom(it.conn_id, m_handle, om);
|
2020-03-30 01:44:20 +02:00
|
|
|
if(rc == 0) {
|
2020-06-08 02:42:28 +02:00
|
|
|
statusRC = NimBLECharacteristicCallbacks::Status::SUCCESS_NOTIFY;
|
2020-03-30 01:44:20 +02:00
|
|
|
} else {
|
2020-06-08 02:42:28 +02:00
|
|
|
statusRC = NimBLECharacteristicCallbacks::Status::ERROR_GATT;
|
2020-03-30 01:44:20 +02:00
|
|
|
}
|
|
|
|
}
|
2020-06-08 02:42:28 +02:00
|
|
|
|
|
|
|
m_pCallbacks->onStatus(this, statusRC, rc);
|
2020-05-14 06:03:56 +02:00
|
|
|
}
|
2020-03-30 01:44:20 +02:00
|
|
|
|
2020-05-14 06:03:56 +02:00
|
|
|
NIMBLE_LOGD(LOG_TAG, "<< notify");
|
2020-03-30 01:44:20 +02:00
|
|
|
} // Notify
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Set the callback handlers for this characteristic.
|
|
|
|
* @param [in] pCallbacks An instance of a callbacks structure used to define any callbacks for the characteristic.
|
|
|
|
*/
|
|
|
|
void NimBLECharacteristic::setCallbacks(NimBLECharacteristicCallbacks* pCallbacks) {
|
2020-05-14 06:03:56 +02:00
|
|
|
if (pCallbacks != nullptr){
|
|
|
|
m_pCallbacks = pCallbacks;
|
|
|
|
} else {
|
|
|
|
m_pCallbacks = &defaultCallback;
|
|
|
|
}
|
2020-03-30 01:44:20 +02:00
|
|
|
} // setCallbacks
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @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.
|
|
|
|
*/
|
2020-05-10 15:21:46 +02:00
|
|
|
void NimBLECharacteristic::setValue(const uint8_t* data, size_t length) {
|
2020-06-08 02:42:28 +02:00
|
|
|
#if CONFIG_LOG_DEFAULT_LEVEL > 3 || (ARDUINO_ARCH_ESP32 && CORE_DEBUG_LEVEL >= 4)
|
2020-05-14 06:03:56 +02:00
|
|
|
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());
|
|
|
|
free(pHex);
|
2020-06-08 02:42:28 +02:00
|
|
|
#endif
|
2020-05-14 06:03:56 +02:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2020-06-08 02:42:28 +02:00
|
|
|
portENTER_CRITICAL(&m_valMux);
|
|
|
|
m_value = std::string((char*)data, length);
|
|
|
|
portEXIT_CRITICAL(&m_valMux);
|
2020-05-14 06:03:56 +02:00
|
|
|
|
|
|
|
NIMBLE_LOGD(LOG_TAG, "<< setValue");
|
2020-03-30 01:44:20 +02:00
|
|
|
} // setValue
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Set the value of the characteristic from string data.
|
|
|
|
* We set the value of the characteristic from the bytes contained in the
|
|
|
|
* string.
|
|
|
|
* @param [in] Set the value of the characteristic.
|
|
|
|
* @return N/A.
|
|
|
|
*/
|
2020-05-10 15:21:46 +02:00
|
|
|
void NimBLECharacteristic::setValue(const std::string &value) {
|
2020-05-14 06:03:56 +02:00
|
|
|
setValue((uint8_t*)(value.data()), value.length());
|
2020-03-30 01:44:20 +02:00
|
|
|
} // setValue
|
|
|
|
|
|
|
|
void NimBLECharacteristic::setValue(uint16_t& data16) {
|
2020-05-14 06:03:56 +02:00
|
|
|
uint8_t temp[2];
|
|
|
|
temp[0] = data16;
|
|
|
|
temp[1] = data16 >> 8;
|
|
|
|
setValue(temp, 2);
|
2020-03-30 01:44:20 +02:00
|
|
|
} // setValue
|
|
|
|
|
|
|
|
void NimBLECharacteristic::setValue(uint32_t& data32) {
|
2020-05-14 06:03:56 +02:00
|
|
|
uint8_t temp[4];
|
|
|
|
temp[0] = data32;
|
|
|
|
temp[1] = data32 >> 8;
|
|
|
|
temp[2] = data32 >> 16;
|
|
|
|
temp[3] = data32 >> 24;
|
|
|
|
setValue(temp, 4);
|
2020-03-30 01:44:20 +02:00
|
|
|
} // setValue
|
|
|
|
|
|
|
|
void NimBLECharacteristic::setValue(int& data32) {
|
2020-05-14 06:03:56 +02:00
|
|
|
uint8_t temp[4];
|
|
|
|
temp[0] = data32;
|
|
|
|
temp[1] = data32 >> 8;
|
|
|
|
temp[2] = data32 >> 16;
|
|
|
|
temp[3] = data32 >> 24;
|
|
|
|
setValue(temp, 4);
|
2020-03-30 01:44:20 +02:00
|
|
|
} // setValue
|
|
|
|
|
|
|
|
void NimBLECharacteristic::setValue(float& data32) {
|
2020-05-14 06:03:56 +02:00
|
|
|
float temp = data32;
|
|
|
|
setValue((uint8_t*)&temp, 4);
|
2020-03-30 01:44:20 +02:00
|
|
|
} // setValue
|
|
|
|
|
|
|
|
void NimBLECharacteristic::setValue(double& data64) {
|
2020-05-14 06:03:56 +02:00
|
|
|
double temp = data64;
|
|
|
|
setValue((uint8_t*)&temp, 8);
|
2020-03-30 01:44:20 +02:00
|
|
|
} // setValue
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Return a string representation of the characteristic.
|
|
|
|
* @return A string representation of the characteristic.
|
|
|
|
*/
|
|
|
|
std::string NimBLECharacteristic::toString() {
|
2020-05-14 06:03:56 +02: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-30 01:44:20 +02:00
|
|
|
} // toString
|
|
|
|
|
|
|
|
|
|
|
|
NimBLECharacteristicCallbacks::~NimBLECharacteristicCallbacks() {}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Callback function to support a read request.
|
|
|
|
* @param [in] pCharacteristic The characteristic that is the source of the event.
|
|
|
|
*/
|
|
|
|
void NimBLECharacteristicCallbacks::onRead(NimBLECharacteristic* pCharacteristic) {
|
2020-05-14 06:03:56 +02:00
|
|
|
NIMBLE_LOGD("NimBLECharacteristicCallbacks", "onRead: default");
|
2020-03-30 01:44:20 +02:00
|
|
|
} // onRead
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Callback function to support a write request.
|
|
|
|
* @param [in] pCharacteristic The characteristic that is the source of the event.
|
|
|
|
*/
|
|
|
|
void NimBLECharacteristicCallbacks::onWrite(NimBLECharacteristic* pCharacteristic) {
|
2020-05-14 06:03:56 +02:00
|
|
|
NIMBLE_LOGD("NimBLECharacteristicCallbacks", "onWrite: default");
|
2020-03-30 01:44:20 +02: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-14 06:03:56 +02:00
|
|
|
NIMBLE_LOGD("NimBLECharacteristicCallbacks", "onNotify: default");
|
2020-03-30 01:44:20 +02:00
|
|
|
} // onNotify
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Callback function to support a Notify/Indicate Status report.
|
|
|
|
* @param [in] pCharacteristic The characteristic that is the source of the event.
|
|
|
|
* @param [in] s Status of the notification/indication
|
|
|
|
* @param [in] code Additional code of underlying errors
|
|
|
|
*/
|
|
|
|
void NimBLECharacteristicCallbacks::onStatus(NimBLECharacteristic* pCharacteristic, Status s, int code) {
|
2020-05-14 06:03:56 +02:00
|
|
|
NIMBLE_LOGD("NimBLECharacteristicCallbacks", "onStatus: default");
|
2020-03-30 01:44:20 +02:00
|
|
|
} // onStatus
|
|
|
|
|
2020-05-14 06:03:56 +02:00
|
|
|
#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
|
2020-04-23 23:17:09 +02:00
|
|
|
#endif /* CONFIG_BT_ENABLED */
|