Implement client long read / write

Client will now read/write long characteristics and descriptors.
This commit is contained in:
h2zero
2020-05-07 19:51:15 -06:00
parent 04b524d1f8
commit 59823b4bf0
4 changed files with 158 additions and 104 deletions
+82 -61
View File
@@ -53,6 +53,8 @@ static const char* LOG_TAG = "NimBLERemoteCharacteristic";
m_charProp = chr->properties;
m_pRemoteService = pRemoteService;
m_notifyCallback = nullptr;
m_rawData = nullptr;
m_dataLen = 0;
} // NimBLERemoteCharacteristic
@@ -61,7 +63,9 @@ static const char* LOG_TAG = "NimBLERemoteCharacteristic";
*/
NimBLERemoteCharacteristic::~NimBLERemoteCharacteristic() {
removeDescriptors(); // Release resources for any descriptor information we may have allocated.
if(m_rawData != nullptr) free(m_rawData);
if(m_rawData != nullptr) {
free(m_rawData);
}
} // ~NimBLERemoteCharacteristic
/*
@@ -319,9 +323,11 @@ std::string NimBLERemoteCharacteristic::readValue() {
int rc = 0;
int retryCount = 1;
// Clear the value before reading.
m_value = "";
NimBLEClient* pClient = getRemoteService()->getClient();
// Check to see that we are connected.
if (!pClient->isConnected()) {
NIMBLE_LOGE(LOG_TAG, "Disconnected");
@@ -330,25 +336,28 @@ std::string NimBLERemoteCharacteristic::readValue() {
do {
m_semaphoreReadCharEvt.take("readValue");
rc = ble_gattc_read(pClient->getConnId(), m_handle,
NimBLERemoteCharacteristic::onReadCB, this);
// long read experiment
/* rc = ble_gattc_read_long(pClient->getConnId(), m_handle, 0,
NimBLERemoteCharacteristic::onReadCB, this);
*/
rc = ble_gattc_read_long(pClient->getConnId(), m_handle, 0,
NimBLERemoteCharacteristic::onReadCB,
this);
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "Error: Failed to read characteristic; rc=%d", rc);
m_semaphoreReadCharEvt.give();
NIMBLE_LOGE(LOG_TAG, "Error: Failed to read characteristic; rc=%d, %s",
rc, NimBLEUtils::returnCodeToString(rc));
m_semaphoreReadCharEvt.give(0);
return "";
}
rc = m_semaphoreReadCharEvt.wait("readValue");
switch(rc){
case 0:
case BLE_HS_EDONE:
rc = 0;
break;
// Characteristic is not long-readable, return with what we have.
case BLE_HS_ATT_ERR(BLE_ATT_ERR_ATTR_NOT_LONG):
NIMBLE_LOGI(LOG_TAG, "Attribute not long");
rc = 0;
break;
case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_AUTHEN):
case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_AUTHOR):
case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_ENC):
@@ -361,7 +370,7 @@ std::string NimBLERemoteCharacteristic::readValue() {
} while(rc != 0 && retryCount--);
NIMBLE_LOGD(LOG_TAG, "<< readValue(): length: %d", m_value.length());
return (rc == 0) ? m_value : "";
return m_value;
} // readValue
@@ -374,36 +383,25 @@ int NimBLERemoteCharacteristic::onReadCB(uint16_t conn_handle,
struct ble_gatt_attr *attr, void *arg)
{
NimBLERemoteCharacteristic* characteristic = (NimBLERemoteCharacteristic*)arg;
uint16_t conn_id = characteristic->getRemoteService()->getClient()->getConnId();
// Make sure the discovery is for this device
if(characteristic->getRemoteService()->getClient()->getConnId() != conn_handle){
// Make sure the read is for this client
if(conn_id != conn_handle) {
return 0;
}
NIMBLE_LOGI(LOG_TAG, "Read complete; status=%d conn_handle=%d", error->status, conn_handle);
// long read experiment
/* if(attr && (attr->om->om_len >= (ble_att_mtu(characteristic->getRemoteService()->getClient()->getConnId()) - 1))){
return 0;
if(error->status == 0) {
if(attr) {
NIMBLE_LOGD(LOG_TAG, "Got %d bytes", attr->om->om_len);
characteristic->m_value += std::string((char*) attr->om->om_data, attr->om->om_len);
return 0;
}
}
*/
if(characteristic->m_rawData != nullptr) {
free(characteristic->m_rawData);
}
if (error->status == 0) {
characteristic->m_value = std::string((char*) attr->om->om_data, attr->om->om_len);
characteristic->m_rawData = (uint8_t*) calloc(attr->om->om_len, sizeof(uint8_t));
memcpy(characteristic->m_rawData, attr->om->om_data, attr->om->om_len);
characteristic->m_semaphoreReadCharEvt.give(0);
} else {
characteristic->m_rawData = nullptr;
characteristic->m_value = "";
characteristic->m_semaphoreReadCharEvt.give(error->status);
}
// characteristic->m_semaphoreReadCharEvt.give(error->status);
return 0; //1
// Read complete release semaphore and let the app can continue.
characteristic->m_semaphoreReadCharEvt.give(error->status);
return 0;
}
@@ -523,7 +521,7 @@ bool NimBLERemoteCharacteristic::writeValue(uint8_t* data, size_t length, bool r
NimBLEClient* pClient = getRemoteService()->getClient();
int rc = 0;
int retryCount = 1;
// uint16_t mtu;
uint16_t mtu;
// Check to see that we are connected.
if (!pClient->isConnected()) {
@@ -531,29 +529,30 @@ bool NimBLERemoteCharacteristic::writeValue(uint8_t* data, size_t length, bool r
return false;
}
// mtu = ble_att_mtu(pClient->getConnId()) - 3;
mtu = ble_att_mtu(pClient->getConnId()) - 3;
if(/*!length > mtu &&*/ !response) {
// Check if the data length is longer than we can write in 1 connection event.
// If so we must do a long write which requires a response.
if(length <= mtu && !response) {
rc = ble_gattc_write_no_rsp_flat(pClient->getConnId(), m_handle, data, length);
return (rc==0);
}
do {
m_semaphoreWriteCharEvt.take("writeValue");
// long write experiment
/* if(length > mtu) {
NIMBLE_LOGD(LOG_TAG,"long write");
if(length > mtu) {
NIMBLE_LOGI(LOG_TAG,"long write %d bytes", length);
os_mbuf *om = ble_hs_mbuf_from_flat(data, length);
rc = ble_gattc_write_long(pClient->getConnId(), m_handle, 0, om,
NimBLERemoteCharacteristic::onWriteCB,
NimBLERemoteCharacteristic::onWriteCB,
this);
} else {
*/
rc = ble_gattc_write_flat(pClient->getConnId(), m_handle,
data, length,
NimBLERemoteCharacteristic::onWriteCB,
this);
// }
rc = ble_gattc_write_flat(pClient->getConnId(), m_handle,
data, length,
NimBLERemoteCharacteristic::onWriteCB,
this);
}
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "Error: Failed to write characteristic; rc=%d", rc);
m_semaphoreWriteCharEvt.give();
@@ -564,6 +563,13 @@ bool NimBLERemoteCharacteristic::writeValue(uint8_t* data, size_t length, bool r
switch(rc){
case 0:
case BLE_HS_EDONE:
rc = 0;
break;
case BLE_HS_ATT_ERR(BLE_ATT_ERR_ATTR_NOT_LONG):
NIMBLE_LOGE(LOG_TAG, "Long write not supported by peer; Truncating length to %d", mtu);
retryCount++;
length = mtu;
break;
case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_AUTHEN):
@@ -592,34 +598,49 @@ int NimBLERemoteCharacteristic::onWriteCB(uint16_t conn_handle,
{
NimBLERemoteCharacteristic* characteristic = (NimBLERemoteCharacteristic*)arg;
// Make sure the discovery is for this device
// Make sure the discovery is for this device
if(characteristic->getRemoteService()->getClient()->getConnId() != conn_handle){
return 0;
}
NIMBLE_LOGI(LOG_TAG, "Write complete; status=%d conn_handle=%d", error->status, conn_handle);
if (error->status == 0) {
characteristic->m_semaphoreWriteCharEvt.give(error->status);
characteristic->m_semaphoreWriteCharEvt.give(0);
} else {
characteristic->m_semaphoreWriteCharEvt.give(error->status);
}
return 0;
}
/**
* @brief Read raw data from remote characteristic as hex bytes
* @return return pointer data read
* @return uint8_t pointer to the data read.
*/
uint8_t* NimBLERemoteCharacteristic::readRawData() {
if(m_rawData != nullptr) {
free(m_rawData);
m_rawData = nullptr;
}
m_dataLen = m_value.length();
// If we have data copy it to rawData
if(m_dataLen) {
m_rawData = (uint8_t*) calloc(m_dataLen, sizeof(uint8_t));
memcpy(m_rawData, m_value.data(), m_dataLen);
}
return m_rawData;
}
/**
* @brief Get the length of the data read from the remote characteristic.
* @return size_t length of the data in bytes.
*/
size_t NimBLERemoteCharacteristic::getDataLength() {
return m_value.length();
}
void NimBLERemoteCharacteristic::releaseSemaphores() {
for (auto &dPair : m_descriptorMap) {
dPair.second->releaseSemaphores();