esp-nimble-cpp/src/NimBLERemoteService.cpp

390 lines
12 KiB
C++
Raw Normal View History

2020-03-30 01:44:20 +02:00
/*
* NimBLERemoteService.cpp
*
* Created: on Jan 27 2020
* Author H2zero
*
2020-03-30 01:44:20 +02:00
* Originally:
*
* BLERemoteService.cpp
*
* Created on: Jul 8, 2017
* Author: kolban
*/
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include "nimconfig.h"
#if defined( CONFIG_BT_NIMBLE_ROLE_CENTRAL)
2020-03-30 01:44:20 +02:00
#include "NimBLERemoteService.h"
#include "NimBLEUtils.h"
#include "NimBLEDevice.h"
#include "NimBLELog.h"
static const char* LOG_TAG = "NimBLERemoteService";
/**
* @brief Remote Service constructor.
* @param [in] Reference to the client this belongs to.
* @param [in] Refernce to the structure with the services' information.
*/
2020-03-30 01:44:20 +02:00
NimBLERemoteService::NimBLERemoteService(NimBLEClient* pClient, const struct ble_gatt_svc* service) {
NIMBLE_LOGD(LOG_TAG, ">> NimBLERemoteService()");
2020-03-30 01:44:20 +02:00
m_pClient = pClient;
switch (service->uuid.u.type) {
case BLE_UUID_TYPE_16:
2020-03-30 01:44:20 +02:00
m_uuid = NimBLEUUID(service->uuid.u16.value);
break;
case BLE_UUID_TYPE_32:
2020-03-30 01:44:20 +02:00
m_uuid = NimBLEUUID(service->uuid.u32.value);
break;
case BLE_UUID_TYPE_128:
2020-03-30 01:44:20 +02:00
m_uuid = NimBLEUUID(const_cast<ble_uuid128_t*>(&service->uuid.u128));
break;
default:
m_uuid = nullptr;
break;
}
m_startHandle = service->start_handle;
m_endHandle = service->end_handle;
NIMBLE_LOGD(LOG_TAG, "<< NimBLERemoteService()");
2020-03-30 01:44:20 +02:00
}
/**
* @brief When deleting the service make sure we delete all characteristics and descriptors.
* Also release any semaphores they may be holding.
*/
2020-03-30 01:44:20 +02:00
NimBLERemoteService::~NimBLERemoteService() {
deleteCharacteristics();
2020-03-30 01:44:20 +02:00
}
/**
* @brief Get iterator to the beginning of the vector of remote characteristic pointers.
* @return An iterator to the beginning of the vector of remote characteristic pointers.
*/
std::vector<NimBLERemoteCharacteristic*>::iterator NimBLERemoteService::begin() {
return m_characteristicVector.begin();
}
/**
* @brief Get iterator to the end of the vector of remote characteristic pointers.
* @return An iterator to the end of the vector of remote characteristic pointers.
*/
std::vector<NimBLERemoteCharacteristic*>::iterator NimBLERemoteService::end() {
return m_characteristicVector.end();
}
2020-03-30 01:44:20 +02:00
/**
* @brief Get the remote characteristic object for the characteristic UUID.
* @param [in] uuid Remote characteristic uuid.
* @return Reference to the remote characteristic object.
*/
2020-03-30 01:44:20 +02:00
NimBLERemoteCharacteristic* NimBLERemoteService::getCharacteristic(const char* uuid) {
return getCharacteristic(NimBLEUUID(uuid));
} // getCharacteristic
2020-03-30 01:44:20 +02:00
/**
* @brief Get the characteristic object for the UUID.
* @param [in] uuid Characteristic uuid.
* @return Reference to the characteristic object, or nullptr if not found.
*/
NimBLERemoteCharacteristic* NimBLERemoteService::getCharacteristic(const NimBLEUUID &uuid) {
for(auto &it: m_characteristicVector) {
if(it->getUUID() == uuid) {
return it;
}
}
size_t prev_size = m_characteristicVector.size();
if(retrieveCharacteristics(&uuid)) {
if(m_characteristicVector.size() > prev_size) {
return m_characteristicVector.back();
2020-03-30 01:44:20 +02:00
}
}
return nullptr;
} // getCharacteristic
/**
* @Get a pointer to the vector of found characteristics.
* @param [in] bool value to indicate if the current vector should be cleared and
* subsequently all characteristics for this service retrieved from the peripheral.
* If false the vector will be returned with the currently stored characteristics,
* If true it will retrieve all characteristics of this service from the peripheral
* and return the vector with all characteristics for this service.
* @return a pointer to the vector of descriptors for this characteristic.
*/
std::vector<NimBLERemoteCharacteristic*>* NimBLERemoteService::getCharacteristics(bool refresh) {
if(refresh) {
deleteCharacteristics();
if (!retrieveCharacteristics()) {
NIMBLE_LOGE(LOG_TAG, "Error: Failed to get characteristics");
}
else{
NIMBLE_LOGI(LOG_TAG, "Found %d characteristics", m_characteristicVector.size());
}
}
return &m_characteristicVector;
} // getCharacteristics
2020-03-30 01:44:20 +02:00
/**
* @brief Callback for Characterisic discovery.
*/
int NimBLERemoteService::characteristicDiscCB(uint16_t conn_handle,
2020-03-30 01:44:20 +02:00
const struct ble_gatt_error *error,
const struct ble_gatt_chr *chr, void *arg)
2020-03-30 01:44:20 +02:00
{
NIMBLE_LOGD(LOG_TAG,"Characteristic Discovered >> status: %d handle: %d",
error->status, (error->status == 0) ? chr->val_handle : -1);
2020-03-30 01:44:20 +02:00
NimBLERemoteService *service = (NimBLERemoteService*)arg;
int rc=0;
// Make sure the discovery is for this device
if(service->getClient()->getConnId() != conn_handle){
return 0;
}
2020-03-30 01:44:20 +02:00
switch (error->status) {
case 0: {
// Found a service - add it to the vector
2020-03-30 01:44:20 +02:00
NimBLERemoteCharacteristic* pRemoteCharacteristic = new NimBLERemoteCharacteristic(service, chr);
service->m_characteristicVector.push_back(pRemoteCharacteristic);
2020-03-30 01:44:20 +02:00
break;
}
case BLE_HS_EDONE:{
/** All characteristics in this service discovered; start discovering
* characteristics in the next service.
*/
service->m_semaphoreGetCharEvt.give(0);
rc = 0;
break;
}
default:
rc = error->status;
break;
}
if (rc != 0) {
/* Error; abort discovery. */
// pass non-zero to semaphore on error to indicate an error finding characteristics
// release memory from any characteristics we created
//service->deleteCharacteristics(); --this will now be done when we clear services on returning with error
2020-03-30 01:44:20 +02:00
NIMBLE_LOGE(LOG_TAG, "characteristicDiscCB() rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc));
service->m_semaphoreGetCharEvt.give(1);
}
NIMBLE_LOGD(LOG_TAG,"<< Characteristic Discovered. status: %d", rc);
return rc;
}
/**
* @brief Retrieve all the characteristics for this service.
* This function will not return until we have all the characteristics.
* @return N/A
*/
bool NimBLERemoteService::retrieveCharacteristics(const NimBLEUUID *uuid_filter) {
2020-03-30 01:44:20 +02:00
NIMBLE_LOGD(LOG_TAG, ">> retrieveCharacteristics() for service: %s", getUUID().toString().c_str());
2020-03-30 01:44:20 +02:00
int rc = 0;
//deleteCharacteristics(); // Forget any previous characteristics.
2020-03-30 01:44:20 +02:00
m_semaphoreGetCharEvt.take("retrieveCharacteristics");
if(uuid_filter == nullptr) {
rc = ble_gattc_disc_all_chrs(m_pClient->getConnId(),
m_startHandle,
m_endHandle,
NimBLERemoteService::characteristicDiscCB,
this);
} else {
rc = ble_gattc_disc_chrs_by_uuid(m_pClient->getConnId(),
m_startHandle,
m_endHandle,
&uuid_filter->getNative()->u,
NimBLERemoteService::characteristicDiscCB,
this);
}
2020-03-30 01:44:20 +02:00
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "ble_gattc_disc_all_chrs: rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc));
m_semaphoreGetCharEvt.give();
return false;
}
if(m_semaphoreGetCharEvt.wait("retrieveCharacteristics") == 0){
2020-03-30 01:44:20 +02:00
NIMBLE_LOGD(LOG_TAG, "<< retrieveCharacteristics()");
return true;
}
2020-03-30 01:44:20 +02:00
NIMBLE_LOGE(LOG_TAG, "Could not retrieve characteristics");
return false;
2020-03-30 01:44:20 +02:00
} // retrieveCharacteristics
/**
* @brief Get the client associated with this service.
* @return A reference to the client associated with this service.
*/
NimBLEClient* NimBLERemoteService::getClient() {
return m_pClient;
} // getClient
/**
* @brief Get the service end handle.
*/
uint16_t NimBLERemoteService::getEndHandle() {
return m_endHandle;
} // getEndHandle
/**
* @brief Get the service start handle.
*/
uint16_t NimBLERemoteService::getStartHandle() {
return m_startHandle;
} // getStartHandle
/**
* @brief Get the service UUID.
*/
NimBLEUUID NimBLERemoteService::getUUID() {
return m_uuid;
}
/**
* @brief Read the value of a characteristic associated with this service.
* @param [in] characteristicUuid The characteristic to read.
* @returns a string containing the value or an empty string if not found or error.
*/
std::string NimBLERemoteService::getValue(const NimBLEUUID &characteristicUuid) {
2020-03-30 01:44:20 +02:00
NIMBLE_LOGD(LOG_TAG, ">> readValue: uuid: %s", characteristicUuid.toString().c_str());
2020-03-30 01:44:20 +02:00
std::string ret = "";
NimBLERemoteCharacteristic* pChar = getCharacteristic(characteristicUuid);
2020-03-30 01:44:20 +02:00
if(pChar != nullptr) {
ret = pChar->readValue();
}
NIMBLE_LOGD(LOG_TAG, "<< readValue");
return ret;
} // readValue
/**
* @brief Set the value of a characteristic.
* @param [in] characteristicUuid The characteristic to set.
* @param [in] value The value to set.
* @returns true on success, false if not found or error
*/
bool NimBLERemoteService::setValue(const NimBLEUUID &characteristicUuid, const std::string &value) {
2020-03-30 01:44:20 +02:00
NIMBLE_LOGD(LOG_TAG, ">> setValue: uuid: %s", characteristicUuid.toString().c_str());
2020-03-30 01:44:20 +02:00
bool ret = false;
NimBLERemoteCharacteristic* pChar = getCharacteristic(characteristicUuid);
2020-03-30 01:44:20 +02:00
if(pChar != nullptr) {
ret = pChar->writeValue(value);
}
NIMBLE_LOGD(LOG_TAG, "<< setValue");
return ret;
} // setValue
/**
* @brief Delete the characteristics in the characteristics vector.
* We maintain a vector called m_characteristicsVector that contains pointers to BLERemoteCharacteristic
* object references. Since we allocated these in this class, we are also responsible for deleting
* them. This method does just that.
2020-03-30 01:44:20 +02:00
* @return N/A.
*/
void NimBLERemoteService::deleteCharacteristics() {
NIMBLE_LOGD(LOG_TAG, ">> deleteCharacteristics");
for(auto &it: m_characteristicVector) {
delete it;
2020-03-30 01:44:20 +02:00
}
m_characteristicVector.clear(); // Clear the vector
NIMBLE_LOGD(LOG_TAG, "<< deleteCharacteristics");
} // deleteCharacteristics
/**
* @brief Delete characteristic by UUID
* @param [in] uuid The UUID of the characteristic to be cleared.
* @return Number of characteristics left.
*/
size_t NimBLERemoteService::deleteCharacteristic(const NimBLEUUID &uuid) {
NIMBLE_LOGD(LOG_TAG, ">> deleteCharacteristic");
// Delete the requested characteristic.
for(auto it = m_characteristicVector.begin(); it != m_characteristicVector.end(); ++it) {
if((*it)->getUUID() == uuid) {
delete *it;
m_characteristicVector.erase(it);
break;
}
}
NIMBLE_LOGD(LOG_TAG, "<< deleteCharacteristic");
return m_characteristicVector.size();
} // deleteCharacteristic
2020-03-30 01:44:20 +02:00
/**
* @brief Create a string representation of this remote service.
* @return A string representation of this remote service.
*/
std::string NimBLERemoteService::toString() {
std::string res = "Service: uuid: " + m_uuid.toString();
char val[6];
res += ", start_handle: ";
snprintf(val, sizeof(val), "%d", m_startHandle);
res += val;
snprintf(val, sizeof(val), "%04x", m_startHandle);
res += " 0x";
res += val;
res += ", end_handle: ";
snprintf(val, sizeof(val), "%d", m_endHandle);
res += val;
snprintf(val, sizeof(val), "%04x", m_endHandle);
res += " 0x";
res += val;
for (auto &it: m_characteristicVector) {
res += "\n" + it->toString();
2020-03-30 01:44:20 +02:00
}
return res;
} // toString
/**
* @brief called when an error occurrs and we need to release the semaphores to resume operations.
* Will release all characteristic and subsequently all descriptor semaphores for this service.
2020-03-30 01:44:20 +02:00
*/
void NimBLERemoteService::releaseSemaphores() {
for(auto &it: m_characteristicVector) {
it->releaseSemaphores();
2020-03-30 01:44:20 +02:00
}
m_semaphoreGetCharEvt.give(1);
}
#endif // #if defined( CONFIG_BT_NIMBLE_ROLE_CENTRAL)
2020-03-30 01:44:20 +02:00
#endif /* CONFIG_BT_ENABLED */