diff --git a/README.md b/README.md index 5ffa3c5..0ba328c 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ # *** UPDATE *** +Server now handles long reads and writes, still work to do on client. -This library is now ready with (mostly) all original BLE library compatiblity. -Check the examples and API_DIFFERENCES document for details of using this library. - -3 simultaneous connections tested stable so far on both client and server. +NEW Client callback created - ```bool onConnParamsUpdateRequest(NimBLEClient* pClient, const ble_gap_upd_params* params)``` +Called when the server wants to change the connection parameters, return true to accept them or false if not. +Check NimBLE_Client.ino example for a demonstration. # esp-nimble-cpp diff --git a/src/NimBLE2902.cpp b/src/NimBLE2902.cpp index d2f5589..b232060 100644 --- a/src/NimBLE2902.cpp +++ b/src/NimBLE2902.cpp @@ -24,9 +24,9 @@ NimBLE2902::NimBLE2902(NimBLECharacteristic* pCharacterisitic) : NimBLEDescriptor(NimBLEUUID((uint16_t) 0x2902), - BLE_GATT_CHR_PROP_READ | - BLE_GATT_CHR_PROP_WRITE, - 2, pCharacterisitic) + BLE_GATT_CHR_F_READ | + BLE_GATT_CHR_F_WRITE, + 2, pCharacterisitic) { uint8_t data[2] = { 0, 0 }; setValue(data, 2); diff --git a/src/NimBLEAdvertising.cpp b/src/NimBLEAdvertising.cpp index fe44be1..bfdf49d 100644 --- a/src/NimBLEAdvertising.cpp +++ b/src/NimBLEAdvertising.cpp @@ -34,12 +34,12 @@ NimBLEAdvertising::NimBLEAdvertising() { memset(&m_scanData, 0, sizeof m_scanData); memset(&m_advParams, 0, sizeof m_advParams); const char *name = ble_svc_gap_device_name(); - + m_advData.name = (uint8_t *)name; m_advData.name_len = strlen(name); m_advData.name_is_complete = 1; m_scanData.tx_pwr_lvl_is_present = 1; - m_scanData.tx_pwr_lvl = BLE_HS_ADV_TX_PWR_LVL_AUTO; + m_scanData.tx_pwr_lvl = NimBLEDevice::getPower(); m_advData.flags = (BLE_HS_ADV_F_DISC_GEN | BLE_HS_ADV_F_BREDR_UNSUP); m_advData.appearance = 0; m_advData.appearance_is_present = 0; @@ -228,11 +228,6 @@ void NimBLEAdvertising::start() { &m_serviceUUIDs[i].getNative()->u16.value, sizeof(uint16_t)); m_advData.uuids16[m_advData.num_uuids16].u.type = BLE_UUID_TYPE_16; - /* - char buf[BLE_UUID_STR_LEN]; - ble_uuid_to_str(&m_advData.uuids16[m_advData.num_uuids16].u, buf); - NIMBLE_LOGI(LOG_TAG, "Advertising UUID: %s", buf); - */ m_advData.uuids16_is_complete = 1; m_advData.num_uuids16++; } @@ -253,12 +248,7 @@ void NimBLEAdvertising::start() { memcpy(&m_advData.uuids32[m_advData.num_uuids32].value, &m_serviceUUIDs[i].getNative()->u32.value, sizeof(uint32_t)); - m_advData.uuids32[m_advData.num_uuids32].u.type = BLE_UUID_TYPE_32; - /* - char buf[BLE_UUID_STR_LEN]; - ble_uuid_to_str(&m_advData.uuids32[m_advData.num_uuids32].u, buf); - NIMBLE_LOGI(LOG_TAG, "Advertising UUID: %s", buf); - */ + m_advData.uuids32[m_advData.num_uuids32].u.type = BLE_UUID_TYPE_32; m_advData.uuids32_is_complete = 1; m_advData.num_uuids32++; } @@ -278,12 +268,7 @@ void NimBLEAdvertising::start() { memcpy(&m_advData.uuids128[m_advData.num_uuids128].value, &m_serviceUUIDs[i].getNative()->u128.value, 16); - m_advData.uuids128[m_advData.num_uuids128].u.type = BLE_UUID_TYPE_128; - /* - char buf[BLE_UUID_STR_LEN]; - ble_uuid_to_str(&m_advData.uuids128[m_advData.num_uuids128].u, buf); - NIMBLE_LOGI(LOG_TAG, "Advertising UUID: %s", buf); - */ + m_advData.uuids128[m_advData.num_uuids128].u.type = BLE_UUID_TYPE_128; m_advData.uuids128_is_complete = 1; m_advData.num_uuids128++; } @@ -331,7 +316,7 @@ void NimBLEAdvertising::start() { // throw the tx power data into the advertisment } else if (payloadLen < 29) { m_advData.tx_pwr_lvl_is_present = 1; - m_advData.tx_pwr_lvl = BLE_HS_ADV_TX_PWR_LVL_AUTO; + m_advData.tx_pwr_lvl = NimBLEDevice::getPower(); } rc = ble_gap_adv_set_fields(&m_advData); @@ -394,11 +379,15 @@ void NimBLEAdvertising::stop() { NIMBLE_LOGD(LOG_TAG, "<< stop"); } // stop -/* + +/** + * Host reset seems to clear advertising data, + * we need clear the flag so it reloads it. + */ void NimBLEAdvertising::onHostReset() { - // m_advSvcsSet = false; + m_advSvcsSet = false; } - */ + /** * @brief Add data to the payload to be advertised. diff --git a/src/NimBLEAdvertising.h b/src/NimBLEAdvertising.h index 9e8f131..5e4d58a 100644 --- a/src/NimBLEAdvertising.h +++ b/src/NimBLEAdvertising.h @@ -90,7 +90,7 @@ public: private: friend class NimBLEDevice; - // void onHostReset(); + void onHostReset(); ble_hs_adv_fields m_advData; ble_hs_adv_fields m_scanData; ble_gap_adv_params m_advParams; diff --git a/src/NimBLECharacteristic.cpp b/src/NimBLECharacteristic.cpp index e58e712..d69fee8 100644 --- a/src/NimBLECharacteristic.cpp +++ b/src/NimBLECharacteristic.cpp @@ -102,7 +102,16 @@ NimBLEDescriptor* NimBLECharacteristic::createDescriptor(const char* uuid, uint3 NimBLEDescriptor* NimBLECharacteristic::createDescriptor(NimBLEUUID uuid, uint32_t properties, uint16_t max_len) { NimBLEDescriptor* pDescriptor = nullptr; if(uuid.equals(NimBLEUUID((uint16_t)0x2902))) { - pDescriptor = new NimBLE2902(this); + 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. + pDescriptor = m_descriptorMap.getByUUID(uuid); + if(pDescriptor == nullptr) { + pDescriptor = new NimBLE2902(this); + } else { + return pDescriptor; + } } else if (uuid.equals(NimBLEUUID((uint16_t)0x2904))) { pDescriptor = new NimBLE2904(this); @@ -204,18 +213,34 @@ int NimBLECharacteristic::handleGapEvent(uint16_t conn_handle, uint16_t attr_han if(ble_uuid_cmp(uuid, &pCharacteristic->getUUID().getNative()->u) == 0){ switch(ctxt->op) { case BLE_GATT_ACCESS_OP_READ_CHR: { - pCharacteristic->m_pCallbacks->onRead(pCharacteristic); + //NIMBLE_LOGD(LOG_TAG, "read char pkthdr len:%d flags:%d", ctxt->om->om_pkthdr_len, ctxt->om->om_flags); + // 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); + } rc = os_mbuf_append(ctxt->om, pCharacteristic->getData(), pCharacteristic->m_value.getLength()); return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; } case BLE_GATT_ACCESS_OP_WRITE_CHR: { + //NIMBLE_LOGD(LOG_TAG, "write char pkthdr len:%d datalen:%d", ctxt->om->om_pkthdr_len, ctxt->om->om_len); if (ctxt->om->om_len > BLE_ATT_ATTR_MAX_LEN) { return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; } - pCharacteristic->setValue(ctxt->om->om_data, ctxt->om->om_len); + //pCharacteristic->setValue(ctxt->om->om_data, ctxt->om->om_len); + pCharacteristic->m_value.addPart(ctxt->om->om_data, ctxt->om->om_len); + os_mbuf *next; + next = SLIST_NEXT(ctxt->om, om_next); + while(next != NULL){ + //NIMBLE_LOGD(LOG_TAG, "Found long write data, len:%d", next->om_len); + pCharacteristic->m_value.addPart(next->om_data, next->om_len); + next = SLIST_NEXT(next, om_next); + } + pCharacteristic->m_value.commit(); pCharacteristic->m_pCallbacks->onWrite(pCharacteristic); + return 0; } default: diff --git a/src/NimBLEClient.cpp b/src/NimBLEClient.cpp index 9422ef7..29582dc 100644 --- a/src/NimBLEClient.cpp +++ b/src/NimBLEClient.cpp @@ -53,7 +53,15 @@ NimBLEClient::NimBLEClient() m_haveServices = false; m_isConnected = false; m_connectTimeout = 30000; - m_pConnParams = nullptr; + + m_pConnParams.scan_itvl = 16; // Scan interval in 0.625ms units (NimBLE Default) + m_pConnParams.scan_window = 16; // Scan window in 0.625ms units (NimBLE Default) + m_pConnParams.itvl_min = BLE_GAP_INITIAL_CONN_ITVL_MIN; // min_int = 0x10*1.25ms = 20ms + m_pConnParams.itvl_max = BLE_GAP_INITIAL_CONN_ITVL_MAX; // max_int = 0x20*1.25ms = 40ms + m_pConnParams.latency = BLE_GAP_INITIAL_CONN_LATENCY; // number of packets allowed to skip (extends max interval) + m_pConnParams.supervision_timeout = BLE_GAP_INITIAL_SUPERVISION_TIMEOUT; // timeout = 400*10ms = 4000ms + m_pConnParams.min_ce_len = BLE_GAP_INITIAL_CONN_MIN_CE_LEN; // Minimum length of connection event in 0.625ms units + m_pConnParams.max_ce_len = BLE_GAP_INITIAL_CONN_MAX_CE_LEN; // Maximum length of connection event in 0.625ms units } // NimBLEClient @@ -69,10 +77,7 @@ NimBLEClient::~NimBLEClient() { if(m_deleteCallbacks) { delete m_pClientCallbacks; } - - if(m_pConnParams != nullptr) { - free(m_pConnParams); - } + } // ~NimBLEClient @@ -127,31 +132,34 @@ bool NimBLEClient::connect(NimBLEAddress address, uint8_t type, bool refreshServ NIMBLE_LOGE(LOG_TAG, "Connection in progress - must wait."); return false; } - + int rc = 0; m_peerAddress = address; ble_addr_t peerAddrt; memcpy(&peerAddrt.val, address.getNative(),6); peerAddrt.type = type; - + NIMBLE_LOGE(LOG_TAG, "taking connect semaphore"); m_semaphoreOpenEvt.take("connect"); - + NIMBLE_LOGE(LOG_TAG, "taken"); /** Try to connect the the advertiser. Allow 30 seconds (30000 ms) for * timeout (default value of m_connectTimeout). * Loop on BLE_HS_EBUSY if the scan hasn't stopped yet. */ do{ - rc = ble_gap_connect(BLE_OWN_ADDR_PUBLIC, &peerAddrt, m_connectTimeout, m_pConnParams, + rc = ble_gap_connect(BLE_OWN_ADDR_PUBLIC, &peerAddrt, m_connectTimeout, &m_pConnParams, NimBLEClient::handleGapEvent, this); + if(rc == BLE_HS_EBUSY) { + vTaskDelay(1); + } }while(rc == BLE_HS_EBUSY); - if (rc != 0) { + if (rc != 0 && rc != BLE_HS_EDONE) { NIMBLE_LOGE(LOG_TAG, "Error: Failed to connect to device; addr_type=%d " "addr=%s, rc=%d; %s", type, m_peerAddress.toString().c_str(), - rc, NimBLEUtils::returnCodeToString(BLE_HS_ATT_ERR(rc))); + rc, NimBLEUtils::returnCodeToString(rc)); m_semaphoreOpenEvt.give(); m_waitingToConnect = false; @@ -165,7 +173,7 @@ bool NimBLEClient::connect(NimBLEAddress address, uint8_t type, bool refreshServ if(rc != 0){ return false; } - + if(refreshServices) { NIMBLE_LOGD(LOG_TAG, "Refreshing Services for: (%s)", address.toString().c_str()); clearServices(); @@ -182,7 +190,7 @@ bool NimBLEClient::connect(NimBLEAddress address, uint8_t type, bool refreshServ NIMBLE_LOGD(LOG_TAG, "Found %d services", getServices()->size()); } } - + m_pClientCallbacks->onConnect(this); NIMBLE_LOGD(LOG_TAG, "<< connect()"); @@ -225,12 +233,16 @@ int NimBLEClient::disconnect(uint8_t reason) { m_isConnected = false; // flag the disconnect now so no calls are performed after rc = ble_gap_terminate(m_conn_id, reason); if(rc != 0){ - NIMBLE_LOGE(LOG_TAG, "ble_gap_terminate failed: rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc)); + NIMBLE_LOGE(LOG_TAG, "ble_gap_terminate failed: rc=%d %s", rc, + NimBLEUtils::returnCodeToString(rc)); } + // Sometimes a disconnect event is not sent so we need to make sure + // the device can be found again. + NimBLEDevice::removeIgnored(m_peerAddress); } - return rc; NIMBLE_LOGD(LOG_TAG, "<< disconnect()"); + return rc; } // disconnect @@ -239,34 +251,23 @@ int NimBLEClient::disconnect(uint8_t reason) { */ void NimBLEClient::setConnectionParams(uint16_t minInterval, uint16_t maxInterval, uint16_t latency, uint16_t timeout, - uint16_t minConnTime, uint16_t maxConnTime) + uint16_t scanInterval, uint16_t scanWindow)/*, + uint16_t minConnTime, uint16_t maxConnTime)*/ { - if(m_pConnParams == nullptr) { - m_pConnParams = (ble_gap_conn_params*)calloc(1, sizeof(ble_gap_conn_params)); - if(m_pConnParams == nullptr) { - NIMBLE_LOGE(LOG_TAG, "setConnectionParams: Error No Mem"); - return; - } - }else if(0 == (minInterval | maxInterval | latency | timeout)) { - free(m_pConnParams); - m_pConnParams = nullptr; - return; - } - m_pConnParams->scan_itvl = 16; // Scan interval in 0.625ms units (NimBLE Default) - m_pConnParams->scan_window = 16; // Scan window in 0.625ms units (NimBLE Default) - m_pConnParams->itvl_min = minInterval; // min_int = 0x10*1.25ms = 20ms - m_pConnParams->itvl_max = maxInterval; // max_int = 0x20*1.25ms = 40ms - m_pConnParams->latency = latency; // number of packets allowed to skip (extends max interval) - m_pConnParams->supervision_timeout = timeout; // timeout = 400*10ms = 4000ms - m_pConnParams->min_ce_len = minConnTime; // Minimum length of connection event in 0.625ms units - m_pConnParams->max_ce_len = maxConnTime; // Maximum length of connection event in 0.625ms units + + m_pConnParams.scan_itvl = scanInterval; // Scan interval in 0.625ms units + m_pConnParams.scan_window = scanWindow; // Scan window in 0.625ms units + m_pConnParams.itvl_min = minInterval; // min_int = 0x10*1.25ms = 20ms + m_pConnParams.itvl_max = maxInterval; // max_int = 0x20*1.25ms = 40ms + m_pConnParams.latency = latency; // number of packets allowed to skip (extends max interval) + m_pConnParams.supervision_timeout = timeout; // timeout = 400*10ms = 4000ms - int rc = NimBLEUtils::checkConnParams(m_pConnParams); - if(rc != 0) { - NIMBLE_LOGE(LOG_TAG,"setConnectionParams : %s", NimBLEUtils::returnCodeToString(rc)); - free(m_pConnParams); - m_pConnParams = nullptr; - } + // These are not used by NimBLE at this time - Must leave at defaults + //m_pConnParams->min_ce_len = minConnTime; // Minimum length of connection event in 0.625ms units + //m_pConnParams->max_ce_len = maxConnTime; // Maximum length of connection event in 0.625ms units + + int rc = NimBLEUtils::checkConnParams(&m_pConnParams); + assert(rc == 0 && "Invalid Connection parameters"); } @@ -274,21 +275,17 @@ void NimBLEClient::setConnectionParams(uint16_t minInterval, uint16_t maxInterva * Update connection parameters can be called only after connection has been established */ void NimBLEClient::updateConnParams(uint16_t minInterval, uint16_t maxInterval, - uint16_t latency, uint16_t timeout, - uint16_t minConnTime, uint16_t maxConnTime) -{ - if(m_pConnParams == nullptr) { - setConnectionParams(minInterval, maxInterval, latency, timeout, minConnTime, maxConnTime); - } - + uint16_t latency, uint16_t timeout) +{ ble_gap_upd_params params; params.latency = latency; params.itvl_max = maxInterval; params.itvl_min = minInterval; - params.supervision_timeout = timeout; - params.min_ce_len = minConnTime; - params.max_ce_len = maxConnTime; + params.supervision_timeout = timeout; + // These are not used by NimBLE at this time - Must leave at defaults + params.min_ce_len = BLE_GAP_INITIAL_CONN_MIN_CE_LEN; + params.max_ce_len = BLE_GAP_INITIAL_CONN_MAX_CE_LEN; int rc = ble_gap_update_params(m_conn_id, ¶ms); if(rc != 0) { @@ -331,7 +328,7 @@ NimBLEAddress NimBLEClient::getPeerAddress() { int NimBLEClient::getRssi() { NIMBLE_LOGD(LOG_TAG, ">> getRssi()"); if (!isConnected()) { - NIMBLE_LOGD(LOG_TAG, "<< getRssi(): Not connected"); + NIMBLE_LOGE(LOG_TAG, "<< getRssi(): Not connected"); return 0; } @@ -466,13 +463,12 @@ int NimBLEClient::serviceDiscoveredCB( // Found a service - add it to the map NimBLERemoteService* pRemoteService = new NimBLERemoteService(peer, service); peer->m_servicesMap.insert(std::pair(pRemoteService->getUUID().toString(), pRemoteService)); - break; } case BLE_HS_EDONE:{ // All services discovered; start discovering characteristics. - NIMBLE_LOGD(LOG_TAG,"Giving search semaphore - completed"); + //NIMBLE_LOGD(LOG_TAG,"Giving search semaphore - completed"); peer->m_semaphoreSearchCmplEvt.give(0); rc = 0; break; @@ -576,6 +572,8 @@ uint16_t NimBLEClient::getMTU() { client->m_isConnected = false; client->m_waitingToConnect=false; + // Remove the device from ignore list so we will scan it again + NimBLEDevice::removeIgnored(client->m_peerAddress); NIMBLE_LOGI(LOG_TAG, "disconnect; reason=%d, %s", event->disconnect.reason, NimBLEUtils::returnCodeToString(event->disconnect.reason)); @@ -604,8 +602,6 @@ uint16_t NimBLEClient::getMTU() { client->m_semaphoreSearchCmplEvt.give(1); client->m_semeaphoreSecEvt.give(1); - // Remove the device from ignore list so we will scan it again - NimBLEDevice::removeIgnored(client->m_peerAddress); client->m_pClientCallbacks->onDisconnect(client); return 0; @@ -633,7 +629,6 @@ uint16_t NimBLEClient::getMTU() { // print_conn_desc(&desc); // MODLOG_DFLT(INFO, "\n"); - //client->m_pClientCallbacks->onConnect(client); // In the case of a multiconnecting device we ignore this device when // scanning since we are already connected to it @@ -645,18 +640,14 @@ uint16_t NimBLEClient::getMTU() { NimBLEUtils::returnCodeToString(rc)); // if error getting mtu indicate a connection error. client->m_semaphoreOpenEvt.give(rc); - } /*else { - client->m_semaphoreOpenEvt.give(0); - }*/ - + } } else { // Connection attempt failed NIMBLE_LOGE(LOG_TAG, "Error: Connection failed; status=%d %s", event->connect.status, NimBLEUtils::returnCodeToString(event->connect.status)); - client->m_semaphoreOpenEvt.give(event->connect.status); } - + client->m_semaphoreOpenEvt.give(event->connect.status); return 0; } // BLE_GAP_EVENT_CONNECT @@ -691,7 +682,7 @@ uint16_t NimBLEClient::getMTU() { return 0; } // BLE_GAP_EVENT_NOTIFY_RX - case BLE_GAP_EVENT_CONN_UPDATE_REQ: + case BLE_GAP_EVENT_CONN_UPDATE_REQ: case BLE_GAP_EVENT_L2CAP_UPDATE_REQ: { if(client->m_conn_id != event->conn_update_req.conn_handle){ return 0; //BLE_HS_ENOTCONN BLE_ATT_ERR_INVALID_HANDLE @@ -702,22 +693,19 @@ uint16_t NimBLEClient::getMTU() { event->conn_update_req.peer_params->itvl_max, event->conn_update_req.peer_params->latency, event->conn_update_req.peer_params->supervision_timeout); - rc = 0; - // if we set connection params and the peer is asking for new ones, reject them. - if(client->m_pConnParams != nullptr) { - - if(event->conn_update_req.peer_params->itvl_min != client->m_pConnParams->itvl_min || - event->conn_update_req.peer_params->itvl_max != client->m_pConnParams->itvl_max || - event->conn_update_req.peer_params->latency != client->m_pConnParams->latency || - event->conn_update_req.peer_params->supervision_timeout != client->m_pConnParams->supervision_timeout) - { - //event->conn_update_req.self_params->itvl_min = 6;//client->m_pConnParams->itvl_min; - rc = BLE_ERR_CONN_PARMS; - } - } - if(rc != 0) { - NIMBLE_LOGD(LOG_TAG, "Rejected peer params"); + + rc = client->m_pClientCallbacks->onConnParamsUpdateRequest(client, + event->conn_update_req.peer_params) ? 0 : BLE_ERR_CONN_PARMS; + + + if(!rc && event->type == BLE_GAP_EVENT_CONN_UPDATE_REQ ) { + event->conn_update_req.self_params->itvl_min = client->m_pConnParams.itvl_min; + event->conn_update_req.self_params->itvl_max = client->m_pConnParams.itvl_max; + event->conn_update_req.self_params->latency = client->m_pConnParams.latency; + event->conn_update_req.self_params->supervision_timeout = client->m_pConnParams.supervision_timeout; } + + NIMBLE_LOGD(LOG_TAG, "%s peer params", (rc == 0) ? "Accepted" : "Rejected"); return rc; } // BLE_GAP_EVENT_CONN_UPDATE_REQ, BLE_GAP_EVENT_L2CAP_UPDATE_REQ @@ -761,7 +749,7 @@ uint16_t NimBLEClient::getMTU() { NIMBLE_LOGI(LOG_TAG, "mtu update event; conn_handle=%d mtu=%d", event->mtu.conn_handle, event->mtu.value); - client->m_semaphoreOpenEvt.give(0); + client->m_semaphoreOpenEvt.give(0); //client->m_mtu = event->mtu.value; return 0; } // BLE_GAP_EVENT_MTU @@ -868,14 +856,19 @@ std::string NimBLEClient::toString() { } // toString -void NimBLEClientCallbacks::onConnect(NimBLEClient *pClient) { +void NimBLEClientCallbacks::onConnect(NimBLEClient* pClient) { NIMBLE_LOGD("NimBLEClientCallbacks", "onConnect: default"); } -void NimBLEClientCallbacks::onDisconnect(NimBLEClient *pClient) { +void NimBLEClientCallbacks::onDisconnect(NimBLEClient* pClient) { NIMBLE_LOGD("NimBLEClientCallbacks", "onDisconnect: default"); } +bool NimBLEClientCallbacks::onConnParamsUpdateRequest(NimBLEClient* pClient, const ble_gap_upd_params* params) { + NIMBLE_LOGD("NimBLEClientCallbacks", "onConnParamsUpdateRequest: default"); + return true; +} + uint32_t NimBLEClientCallbacks::onPassKeyRequest(){ NIMBLE_LOGD("NimBLEClientCallbacks", "onPassKeyRequest: default: 123456"); return 123456; @@ -889,7 +882,7 @@ bool NimBLEClientCallbacks::onSecurityRequest(){ NIMBLE_LOGD("NimBLEClientCallbacks", "onSecurityRequest: default: true"); return true; } -void NimBLEClientCallbacks::onAuthenticationComplete(ble_gap_conn_desc*){ +void NimBLEClientCallbacks::onAuthenticationComplete(ble_gap_conn_desc* desc){ NIMBLE_LOGD("NimBLEClientCallbacks", "onAuthenticationComplete: default"); } bool NimBLEClientCallbacks::onConfirmPIN(uint32_t pin){ diff --git a/src/NimBLEClient.h b/src/NimBLEClient.h index b8219bc..3d3613c 100644 --- a/src/NimBLEClient.h +++ b/src/NimBLEClient.h @@ -52,14 +52,11 @@ public: void setConnectTimeout(uint8_t timeout); void setConnectionParams(uint16_t minInterval, uint16_t maxInterval, uint16_t latency, uint16_t timeout, - uint16_t minConnTime=16, uint16_t maxConnTime=768); + uint16_t scanInterval=16, uint16_t scanWindow=16); // NimBLE default scan settings void updateConnParams(uint16_t minInterval, uint16_t maxInterval, - uint16_t latency, uint16_t timeout, - uint16_t minConnTime=16, uint16_t maxConnTime=768); + uint16_t latency, uint16_t timeout); - - private: NimBLEClient(); ~NimBLEClient(); @@ -79,10 +76,8 @@ private: bool m_waitingToConnect =false; bool m_deleteCallbacks = true; int32_t m_connectTimeout; - ble_gap_conn_params* m_pConnParams; //uint16_t m_mtu = 23; - NimBLEClientCallbacks* m_pClientCallbacks = nullptr; FreeRTOS::Semaphore m_semaphoreOpenEvt = FreeRTOS::Semaphore("OpenEvt"); @@ -90,6 +85,10 @@ private: FreeRTOS::Semaphore m_semeaphoreSecEvt = FreeRTOS::Semaphore("Security"); std::map m_servicesMap; + +private: + friend class NimBLEClientCallbacks; + ble_gap_conn_params m_pConnParams; }; // class NimBLEClient @@ -100,13 +99,14 @@ private: class NimBLEClientCallbacks { public: virtual ~NimBLEClientCallbacks() {}; - virtual void onConnect(NimBLEClient *pClient); // = 0; - virtual void onDisconnect(NimBLEClient *pClient); // = 0; - virtual uint32_t onPassKeyRequest(); //{return 0;} - virtual void onPassKeyNotify(uint32_t pass_key); //{} - virtual bool onSecurityRequest(); //{return false;} - virtual void onAuthenticationComplete(ble_gap_conn_desc*); //{}; - virtual bool onConfirmPIN(uint32_t pin); //{return false;} + virtual void onConnect(NimBLEClient* pClient); + virtual void onDisconnect(NimBLEClient* pClient); + virtual bool onConnParamsUpdateRequest(NimBLEClient* pClient, const ble_gap_upd_params* params); + virtual uint32_t onPassKeyRequest(); + virtual void onPassKeyNotify(uint32_t pass_key); + virtual bool onSecurityRequest(); + virtual void onAuthenticationComplete(ble_gap_conn_desc* desc); + virtual bool onConfirmPIN(uint32_t pin); }; #endif // CONFIG_BT_ENABLED diff --git a/src/NimBLEDevice.cpp b/src/NimBLEDevice.cpp index 84dce51..078c89c 100644 --- a/src/NimBLEDevice.cpp +++ b/src/NimBLEDevice.cpp @@ -239,17 +239,41 @@ void NimBLEDevice::stopAdvertising() { * * ESP_PWR_LVL_P9 = 7, !< Corresponding to +9dbm * @param [in] powerLevel. */ -/* STATIC */ void NimBLEDevice::setPower(esp_power_level_t powerLevel) { - NIMBLE_LOGD(LOG_TAG, ">> setPower: %d", powerLevel); - esp_err_t errRc = ::esp_ble_tx_power_set(ESP_BLE_PWR_TYPE_DEFAULT, powerLevel); +/* STATIC */ void NimBLEDevice::setPower(esp_power_level_t powerLevel, esp_ble_power_type_t powerType) { + NIMBLE_LOGD(LOG_TAG, ">> setPower: %d (type: %d)", powerLevel, powerType); + esp_err_t errRc = esp_ble_tx_power_set(powerType, powerLevel); if (errRc != ESP_OK) { - //NIMBLE_LOGE(LOG_TAG, "esp_ble_tx_power_set: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); NIMBLE_LOGE(LOG_TAG, "esp_ble_tx_power_set: rc=%d", errRc); - }; + } NIMBLE_LOGD(LOG_TAG, "<< setPower"); } // setPower +/* STATIC */ int NimBLEDevice::getPower(esp_ble_power_type_t powerType) { + + switch(esp_ble_tx_power_get(powerType)) { + case ESP_PWR_LVL_N12: + return -12; + case ESP_PWR_LVL_N9: + return -9; + case ESP_PWR_LVL_N6: + return -6; + case ESP_PWR_LVL_N3: + return -6; + case ESP_PWR_LVL_N0: + return 0; + case ESP_PWR_LVL_P3: + return 3; + case ESP_PWR_LVL_P6: + return 6; + case ESP_PWR_LVL_P9: + return 9; + default: + return BLE_HS_ADV_TX_PWR_LVL_AUTO; + } +} // setPower + + /** * @brief Get our device address. * @return A NimBLEAddress object of our public address if we have one, @@ -327,11 +351,11 @@ void NimBLEDevice::stopAdvertising() { for(auto it = m_cList.cbegin(); it != m_cList.cend(); ++it) { (*it)->onHostReset(); } - +*/ if(m_bleAdvertising != nullptr) { m_bleAdvertising->onHostReset(); } -*/ + NIMBLE_LOGC(LOG_TAG, "Resetting state; reason=%d, %s", reason, NimBLEUtils::returnCodeToString(reason)); } // onReset @@ -414,7 +438,7 @@ void NimBLEDevice::stopAdvertising() { ble_hs_cfg.sync_cb = NimBLEDevice::onSync; // Set initial security capabilities - ble_hs_cfg.sm_io_cap = BLE_SM_IO_CAP_NO_IO; + ble_hs_cfg.sm_io_cap = BLE_HS_IO_NO_INPUT_OUTPUT; ble_hs_cfg.sm_bonding = 0; ble_hs_cfg.sm_mitm = 0; ble_hs_cfg.sm_sc = 1; diff --git a/src/NimBLEDevice.h b/src/NimBLEDevice.h index 903fef3..b2731c2 100644 --- a/src/NimBLEDevice.h +++ b/src/NimBLEDevice.h @@ -59,7 +59,11 @@ #define BLEEddystoneTLM NimBLEEddystoneTLM #define BLEEddystoneURL NimBLEEddystoneURL +#ifdef CONFIG_BT_NIMBLE_MAX_CONNECTIONS #define NIMBLE_MAX_CONNECTIONS CONFIG_BT_NIMBLE_MAX_CONNECTIONS +#else +#define NIMBLE_MAX_CONNECTIONS CONFIG_NIMBLE_MAX_CONNECTIONS +#endif /** * @brief BLE functions. @@ -81,7 +85,8 @@ public: static NimBLEClient* createClient(); static NimBLEServer* createServer(); static bool deleteClient(NimBLEClient* pClient); - static void setPower(esp_power_level_t powerLevel); + static void setPower(esp_power_level_t powerLevel, esp_ble_power_type_t powerType=ESP_BLE_PWR_TYPE_DEFAULT); + static int getPower(esp_ble_power_type_t powerType=ESP_BLE_PWR_TYPE_DEFAULT); static void setCustomGapHandler(gap_event_handler handler); static void setSecurityAuth(bool bonding, bool mitm, bool sc); static void setSecurityAuth(uint8_t auth_req); @@ -110,6 +115,7 @@ private: friend class NimBLEClient; friend class NimBLEScan; friend class NimBLEAdvertising; + friend class NimBLECharacteristic; static void onReset(int reason); static void onSync(void); diff --git a/src/NimBLELog.h b/src/NimBLELog.h index 348da13..d223a04 100644 --- a/src/NimBLELog.h +++ b/src/NimBLELog.h @@ -18,6 +18,9 @@ // Note: because CONFIG_LOG_DEFAULT_LEVEL is set at ERROR in Arduino we must use MODLOG_DFLT(ERROR // otherwise no messages will be printed above that level. #ifdef ARDUINO_ARCH_ESP32 +#ifndef CORE_DEBUG_LEVEL +#define CORE_DEBUG_LEVEL CONFIG_ARDUHAL_LOG_DEFAULT_LEVEL +#endif #if CORE_DEBUG_LEVEL >= 4 #define NIMBLE_LOGD( tag, format, ... ) MODLOG_DFLT(ERROR, "D %s: "#format"\n",tag,##__VA_ARGS__) diff --git a/src/NimBLERemoteCharacteristic.cpp b/src/NimBLERemoteCharacteristic.cpp index bf60d81..a48354c 100644 --- a/src/NimBLERemoteCharacteristic.cpp +++ b/src/NimBLERemoteCharacteristic.cpp @@ -16,8 +16,6 @@ #if defined(CONFIG_BT_ENABLED) #include "NimBLERemoteCharacteristic.h" - -#include #include "NimBLEUtils.h" #include "NimBLELog.h" @@ -321,6 +319,7 @@ std::string NimBLERemoteCharacteristic::readValue() { int rc = 0; int retryCount = 1; + NimBLEClient* pClient = getRemoteService()->getClient(); // Check to see that we are connected. @@ -334,19 +333,22 @@ std::string NimBLERemoteCharacteristic::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); +*/ if (rc != 0) { NIMBLE_LOGE(LOG_TAG, "Error: Failed to read characteristic; rc=%d", rc); - //goto err; m_semaphoreReadCharEvt.give(); return ""; } rc = m_semaphoreReadCharEvt.wait("readValue"); - switch(rc){ case 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): @@ -377,8 +379,13 @@ int NimBLERemoteCharacteristic::onReadCB(uint16_t conn_handle, if(characteristic->getRemoteService()->getClient()->getConnId() != 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(characteristic->m_rawData != nullptr) { free(characteristic->m_rawData); @@ -386,16 +393,17 @@ int NimBLERemoteCharacteristic::onReadCB(uint16_t conn_handle, if (error->status == 0) { characteristic->m_value = std::string((char*) attr->om->om_data, attr->om->om_len); - characteristic->m_semaphoreReadCharEvt.give(0); 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); } - return 0; +// characteristic->m_semaphoreReadCharEvt.give(error->status); + return 0; //1 } @@ -515,6 +523,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; // Check to see that we are connected. if (!pClient->isConnected()) { @@ -522,18 +531,29 @@ bool NimBLERemoteCharacteristic::writeValue(uint8_t* data, size_t length, bool r return false; } - if(!response) { +// mtu = ble_att_mtu(pClient->getConnId()) - 3; + + 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"); + os_mbuf *om = ble_hs_mbuf_from_flat(data, length); + rc = ble_gattc_write_long(pClient->getConnId(), m_handle, 0, om, + NimBLERemoteCharacteristic::onWriteCB, + this); + } else { +*/ rc = ble_gattc_write_flat(pClient->getConnId(), m_handle, data, length, NimBLERemoteCharacteristic::onWriteCB, - this);; + this); +// } if (rc != 0) { NIMBLE_LOGE(LOG_TAG, "Error: Failed to write characteristic; rc=%d", rc); m_semaphoreWriteCharEvt.give(); @@ -558,7 +578,7 @@ bool NimBLERemoteCharacteristic::writeValue(uint8_t* data, size_t length, bool r } while(rc != 0 && retryCount--); NIMBLE_LOGD(LOG_TAG, "<< writeValue, rc: %d",rc); - return (rc == 0); //true; + return (rc == 0); } // writeValue @@ -587,7 +607,7 @@ int NimBLERemoteCharacteristic::onWriteCB(uint16_t conn_handle, characteristic->m_semaphoreWriteCharEvt.give(error->status); } - return error->status; + return 0; } diff --git a/src/NimBLEScan.cpp b/src/NimBLEScan.cpp index 634a953..e5cbed1 100644 --- a/src/NimBLEScan.cpp +++ b/src/NimBLEScan.cpp @@ -231,14 +231,18 @@ bool NimBLEScan::start(uint32_t duration, void (*scanCompleteCB)(NimBLEScanResul return false; } + if(ble_gap_conn_active()) { + NIMBLE_LOGE(LOG_TAG, "Connection in progress - must wait."); + return false; + } + // If we are already scanning don't start again or we will get stuck on the semaphore. if(!m_stopped || ble_gap_disc_active()) { // double check - can cause host reset. NIMBLE_LOGE(LOG_TAG, "Scan already in progress"); return false; } - m_stopped = false; - + m_stopped = false; m_semaphoreScanEnd.take("start"); // Save the callback to be invoked when the scan completes. @@ -261,13 +265,15 @@ bool NimBLEScan::start(uint32_t duration, void (*scanCompleteCB)(NimBLEScanResul } int rc = 0; - do{ rc = ble_gap_disc(m_own_addr_type, duration, &m_scan_params, NimBLEScan::handleGapEvent, this); - }while(rc == BLE_HS_EBUSY); + if(rc == BLE_HS_EBUSY) { + vTaskDelay(2); + } + } while(rc == BLE_HS_EBUSY); - if (rc != 0) { + if (rc != 0 && rc != BLE_HS_EDONE) { NIMBLE_LOGE(LOG_TAG, "Error initiating GAP discovery procedure; rc=%d, %s", rc, NimBLEUtils::returnCodeToString(rc)); m_stopped = true; @@ -275,8 +281,6 @@ bool NimBLEScan::start(uint32_t duration, void (*scanCompleteCB)(NimBLEScanResul return false; } - // m_stopped = false; - NIMBLE_LOGD(LOG_TAG, "<< start()"); return true; } // start diff --git a/src/NimBLEServer.cpp b/src/NimBLEServer.cpp index 3978dae..acef95f 100644 --- a/src/NimBLEServer.cpp +++ b/src/NimBLEServer.cpp @@ -137,7 +137,7 @@ void NimBLEServer::start() { abort(); } -#if CONFIG_LOG_DEFAULT_LEVEL > 3 || ARDUHAL_LOG_LEVEL_INFO > 3 +#if CONFIG_LOG_DEFAULT_LEVEL > 3 || (ARDUINO_ARCH_ESP32 && CORE_DEBUG_LEVEL >= 4) ble_gatts_show_local(); #endif