From 688c0b1a9fe1aed399e8595cd55043083e07343a Mon Sep 17 00:00:00 2001 From: Simon Hailes Date: Thu, 21 Jan 2021 16:41:41 +0000 Subject: [PATCH] NimBLE release 1.1.0 --- lib/libesp32/NimBLE-Arduino/CHANGELOG.md | 2 +- lib/libesp32/NimBLE-Arduino/README.md | 4 + .../BLE_Beacon_Scanner/BLE_Beacon_Scanner.ino | 31 +- .../examples/NimBLE_Client/NimBLE_Client.ino | 294 +++++++++--------- .../NimBLE-Arduino/library.properties | 2 +- .../NimBLE-Arduino/src/NimBLEClient.cpp | 108 ++++--- .../NimBLE-Arduino/src/NimBLEHIDDevice.cpp | 88 +++--- .../NimBLE-Arduino/src/NimBLEHIDDevice.h | 4 + .../NimBLE-Arduino/src/NimBLEServer.cpp | 8 +- 9 files changed, 290 insertions(+), 251 deletions(-) diff --git a/lib/libesp32/NimBLE-Arduino/CHANGELOG.md b/lib/libesp32/NimBLE-Arduino/CHANGELOG.md index 128d3c93d..e763a3c6d 100644 --- a/lib/libesp32/NimBLE-Arduino/CHANGELOG.md +++ b/lib/libesp32/NimBLE-Arduino/CHANGELOG.md @@ -2,7 +2,7 @@ All notable changes to this project will be documented in this file. -## [Unreleased] +## [1.1.0] - 2021-01-20 ### Added - `NimBLEDevice::setOwnAddrType` added to enable the use of random and random-resolvable addresses, by asukiaaa diff --git a/lib/libesp32/NimBLE-Arduino/README.md b/lib/libesp32/NimBLE-Arduino/README.md index ea28b8811..08e2a69c0 100644 --- a/lib/libesp32/NimBLE-Arduino/README.md +++ b/lib/libesp32/NimBLE-Arduino/README.md @@ -1,5 +1,7 @@ [Latest release ![Release Version](https://img.shields.io/github/release/h2zero/NimBLE-Arduino.svg?style=plastic) ![Release Date](https://img.shields.io/github/release-date/h2zero/NimBLE-Arduino.svg?style=plastic)](https://github.com/h2zero/NimBLE-Arduino/releases/latest/) + +Need help? Have questions or suggestions? Join the [![Gitter](https://badges.gitter.im/NimBLE-Arduino/community.svg)](https://gitter.im/NimBLE-Arduino/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
# NimBLE-Arduino @@ -57,6 +59,8 @@ Also see [Improvements_and_updates](docs/Improvements_and_updates.md) for inform [Full API documentation and class list can be found here.](https://h2zero.github.io/esp-nimble-cpp/) +For added performance and optimizations see [Usage tips](docs/Usage_tips.md). + Check the Refactored_original_examples in the examples folder for highlights of the differences with the original library. More advanced examples highlighting many available features are in examples/ NimBLE_Server, NimBLE_Client. diff --git a/lib/libesp32/NimBLE-Arduino/examples/BLE_Beacon_Scanner/BLE_Beacon_Scanner.ino b/lib/libesp32/NimBLE-Arduino/examples/BLE_Beacon_Scanner/BLE_Beacon_Scanner.ino index fea6b7d50..4161aabf7 100644 --- a/lib/libesp32/NimBLE-Arduino/examples/BLE_Beacon_Scanner/BLE_Beacon_Scanner.ino +++ b/lib/libesp32/NimBLE-Arduino/examples/BLE_Beacon_Scanner/BLE_Beacon_Scanner.ino @@ -79,27 +79,24 @@ class MyAdvertisedDeviceCallbacks : public BLEAdvertisedDeviceCallbacks return; } - uint8_t *payLoad = advertisedDevice->getPayload(); + BLEUUID eddyUUID = (uint16_t)0xfeaa; - BLEUUID checkUrlUUID = (uint16_t)0xfeaa; - - if (advertisedDevice->getServiceUUID().equals(checkUrlUUID)) + if (advertisedDevice->getServiceUUID().equals(eddyUUID)) { - if (payLoad[11] == 0x10) + std::string serviceData = advertisedDevice->getServiceData(eddyUUID); + if (serviceData[0] == 0x10) { Serial.println("Found an EddystoneURL beacon!"); BLEEddystoneURL foundEddyURL = BLEEddystoneURL(); - std::string eddyContent((char *)&payLoad[11]); // incomplete EddystoneURL struct! - foundEddyURL.setData(eddyContent); + foundEddyURL.setData(serviceData); std::string bareURL = foundEddyURL.getURL(); if (bareURL[0] == 0x00) { - size_t payLoadLen = advertisedDevice->getPayloadLength(); Serial.println("DATA-->"); - for (int idx = 0; idx < payLoadLen; idx++) + for (int idx = 0; idx < serviceData.length(); idx++) { - Serial.printf("0x%08X ", payLoad[idx]); + Serial.printf("0x%08X ", serviceData[idx]); } Serial.println("\nInvalid Data"); return; @@ -110,23 +107,15 @@ class MyAdvertisedDeviceCallbacks : public BLEAdvertisedDeviceCallbacks Serial.printf("TX power %d\n", foundEddyURL.getPower()); Serial.println("\n"); } - else if (payLoad[11] == 0x20) + else if (serviceData[0] == 0x20) { Serial.println("Found an EddystoneTLM beacon!"); BLEEddystoneTLM foundEddyURL = BLEEddystoneTLM(); - std::string eddyContent((char *)&payLoad[11]); // incomplete EddystoneURL struct! + foundEddyURL.setData(serviceData); - eddyContent = "01234567890123"; - - for (int idx = 0; idx < 14; idx++) - { - eddyContent[idx] = payLoad[idx + 11]; - } - - foundEddyURL.setData(eddyContent); Serial.printf("Reported battery voltage: %dmV\n", foundEddyURL.getVolt()); Serial.printf("Reported temperature from TLM class: %.2fC\n", (double)foundEddyURL.getTemp()); - int temp = (int)payLoad[16] + (int)(payLoad[15] << 8); + int temp = (int)serviceData[5] + (int)(serviceData[4] << 8); float calcTemp = temp / 256.0f; Serial.printf("Reported temperature from data: %.2fC\n", calcTemp); Serial.printf("Reported advertise count: %d\n", foundEddyURL.getCount()); diff --git a/lib/libesp32/NimBLE-Arduino/examples/NimBLE_Client/NimBLE_Client.ino b/lib/libesp32/NimBLE-Arduino/examples/NimBLE_Client/NimBLE_Client.ino index 9ef41b91c..a3899cddc 100644 --- a/lib/libesp32/NimBLE-Arduino/examples/NimBLE_Client/NimBLE_Client.ino +++ b/lib/libesp32/NimBLE-Arduino/examples/NimBLE_Client/NimBLE_Client.ino @@ -2,10 +2,10 @@ /** NimBLE_Server Demo: * * Demonstrates many of the available features of the NimBLE client library. - * + * * Created: on March 24 2020 * Author: H2zero - * + * */ #include @@ -19,15 +19,15 @@ static uint32_t scanTime = 0; /** 0 = scan forever */ /** None of these are required as they will be handled by the library with defaults. ** - ** Remove as you see fit for your needs */ + ** Remove as you see fit for your needs */ class ClientCallbacks : public NimBLEClientCallbacks { void onConnect(NimBLEClient* pClient) { Serial.println("Connected"); /** After connection we should change the parameters if we don't need fast response times. - * These settings are 150ms interval, 0 latency, 450ms timout. + * These settings are 150ms interval, 0 latency, 450ms timout. * Timeout should be a multiple of the interval, minimum is 100ms. * I find a multiple of 3-5 * the interval works best for quick response/reconnect. - * Min interval: 120 * 1.25ms = 150, Max interval: 120 * 1.25ms = 150, 0 latency, 60 * 10ms = 600ms timeout + * Min interval: 120 * 1.25ms = 150, Max interval: 120 * 1.25ms = 150, 0 latency, 60 * 10ms = 600ms timeout */ pClient->updateConnParams(120,120,0,60); }; @@ -37,9 +37,9 @@ class ClientCallbacks : public NimBLEClientCallbacks { Serial.println(" Disconnected - Starting scan"); NimBLEDevice::getScan()->start(scanTime, scanEndedCB); }; - + /** Called when the peripheral requests a change to the connection parameters. - * Return true to accept and apply them or false to reject and keep + * Return true to accept and apply them or false to reject and keep * the currently used parameters. Default will return true. */ bool onConnParamsUpdateRequest(NimBLEClient* pClient, const ble_gap_upd_params* params) { @@ -55,7 +55,7 @@ class ClientCallbacks : public NimBLEClientCallbacks { return true; }; - + /********************* Security handled here ********************** ****** Note: these are the same return values as defaults ********/ uint32_t onPassKeyRequest(){ @@ -85,7 +85,7 @@ class ClientCallbacks : public NimBLEClientCallbacks { /** Define a class to handle the callbacks when advertisments are received */ class AdvertisedDeviceCallbacks: public NimBLEAdvertisedDeviceCallbacks { - + void onResult(NimBLEAdvertisedDevice* advertisedDevice) { Serial.print("Advertised Device found: "); Serial.println(advertisedDevice->toString().c_str()); @@ -94,9 +94,9 @@ class AdvertisedDeviceCallbacks: public NimBLEAdvertisedDeviceCallbacks { Serial.println("Found Our Service"); /** stop scan before connecting */ NimBLEDevice::getScan()->stop(); - /** Save the device reference in a global for the client to use*/ + /** Save the device reference in a global for the client to use*/ advDevice = advertisedDevice; - /** Ready to connect now */ + /** Ready to connect now */ doConnect = true; } }; @@ -105,7 +105,7 @@ class AdvertisedDeviceCallbacks: public NimBLEAdvertisedDeviceCallbacks { /** Notification / Indication receiving handler callback */ void notifyCB(NimBLERemoteCharacteristic* pRemoteCharacteristic, uint8_t* pData, size_t length, bool isNotify){ - std::string str = (isNotify == true) ? "Notification" : "Indication"; + std::string str = (isNotify == true) ? "Notification" : "Indication"; str += " from "; /** NimBLEAddress and NimBLEUUID have std::string operators */ str += std::string(pRemoteCharacteristic->getRemoteService()->getClient()->getPeerAddress()); @@ -128,10 +128,10 @@ static ClientCallbacks clientCB; /** Handles the provisioning of clients and connects / interfaces with the server */ bool connectToServer() { NimBLEClient* pClient = nullptr; - + /** Check if we have a client we should reuse first **/ if(NimBLEDevice::getClientListSize()) { - /** Special case when we already know this device, we send false as the + /** Special case when we already know this device, we send false as the * second argument in connect() to prevent refreshing the service database. * This saves considerable time and power. */ @@ -142,7 +142,7 @@ bool connectToServer() { return false; } Serial.println("Reconnected client"); - } + } /** We don't already have a client that knows this device, * we will check for a client that is disconnected that we can use. */ @@ -150,28 +150,28 @@ bool connectToServer() { pClient = NimBLEDevice::getDisconnectedClient(); } } - + /** No client to reuse? Create a new one. */ if(!pClient) { if(NimBLEDevice::getClientListSize() >= NIMBLE_MAX_CONNECTIONS) { Serial.println("Max clients reached - no more connections available"); return false; } - + pClient = NimBLEDevice::createClient(); - + Serial.println("New client created"); - + pClient->setClientCallbacks(&clientCB, false); - /** Set initial connection parameters: These settings are 15ms interval, 0 latency, 120ms timout. - * These settings are safe for 3 clients to connect reliably, can go faster if you have less + /** Set initial connection parameters: These settings are 15ms interval, 0 latency, 120ms timout. + * These settings are safe for 3 clients to connect reliably, can go faster if you have less * connections. Timeout should be a multiple of the interval, minimum is 100ms. - * Min interval: 12 * 1.25ms = 15, Max interval: 12 * 1.25ms = 15, 0 latency, 51 * 10ms = 510ms timeout + * Min interval: 12 * 1.25ms = 15, Max interval: 12 * 1.25ms = 15, 0 latency, 51 * 10ms = 510ms timeout */ pClient->setConnectionParams(12,12,0,51); /** Set how long we are willing to wait for the connection to complete (seconds), default is 30. */ pClient->setConnectTimeout(5); - + if (!pClient->connect(advDevice)) { /** Created a client but failed to connect, don't need to keep it as it has no data */ @@ -179,149 +179,147 @@ bool connectToServer() { Serial.println("Failed to connect, deleted client"); return false; } - } - + } + if(!pClient->isConnected()) { if (!pClient->connect(advDevice)) { Serial.println("Failed to connect"); return false; } } - + Serial.print("Connected to: "); Serial.println(pClient->getPeerAddress().toString().c_str()); Serial.print("RSSI: "); Serial.println(pClient->getRssi()); - + /** Now we can read/write/subscribe the charateristics of the services we are interested in */ NimBLERemoteService* pSvc = nullptr; NimBLERemoteCharacteristic* pChr = nullptr; NimBLERemoteDescriptor* pDsc = nullptr; - + pSvc = pClient->getService("DEAD"); if(pSvc) { /** make sure it's not null */ pChr = pSvc->getCharacteristic("BEEF"); - } - if(pChr) { /** make sure it's not null */ - if(pChr->canRead()) { - Serial.print(pChr->getUUID().toString().c_str()); - Serial.print(" Value: "); - Serial.println(pChr->readValue().c_str()); - } - - if(pChr->canWrite()) { - if(pChr->writeValue("Tasty")) { - Serial.print("Wrote new value to: "); - Serial.println(pChr->getUUID().toString().c_str()); - } - else { - /** Disconnect if write failed */ - pClient->disconnect(); - return false; - } - + if(pChr) { /** make sure it's not null */ if(pChr->canRead()) { - Serial.print("The value of: "); Serial.print(pChr->getUUID().toString().c_str()); - Serial.print(" is now: "); + Serial.print(" Value: "); Serial.println(pChr->readValue().c_str()); } - } - - /** registerForNotify() has been deprecated and replaced with subscribe() / unsubscribe(). - * Subscribe parameter defaults are: notifications=true, notifyCallback=nullptr, response=false. - * Unsubscribe parameter defaults are: response=false. - */ - if(pChr->canNotify()) { - //if(!pChr->registerForNotify(notifyCB)) { - if(!pChr->subscribe(true, notifyCB)) { - /** Disconnect if subscribe failed */ - pClient->disconnect(); - return false; + + if(pChr->canWrite()) { + if(pChr->writeValue("Tasty")) { + Serial.print("Wrote new value to: "); + Serial.println(pChr->getUUID().toString().c_str()); + } + else { + /** Disconnect if write failed */ + pClient->disconnect(); + return false; + } + + if(pChr->canRead()) { + Serial.print("The value of: "); + Serial.print(pChr->getUUID().toString().c_str()); + Serial.print(" is now: "); + Serial.println(pChr->readValue().c_str()); + } + } + + /** registerForNotify() has been deprecated and replaced with subscribe() / unsubscribe(). + * Subscribe parameter defaults are: notifications=true, notifyCallback=nullptr, response=false. + * Unsubscribe parameter defaults are: response=false. + */ + if(pChr->canNotify()) { + //if(!pChr->registerForNotify(notifyCB)) { + if(!pChr->subscribe(true, notifyCB)) { + /** Disconnect if subscribe failed */ + pClient->disconnect(); + return false; + } + } + else if(pChr->canIndicate()) { + /** Send false as first argument to subscribe to indications instead of notifications */ + //if(!pChr->registerForNotify(notifyCB, false)) { + if(!pChr->subscribe(false, notifyCB)) { + /** Disconnect if subscribe failed */ + pClient->disconnect(); + return false; + } } } - else if(pChr->canIndicate()) { - /** Send false as first argument to subscribe to indications instead of notifications */ - //if(!pChr->registerForNotify(notifyCB, false)) { - if(!pChr->subscribe(false, notifyCB)) { - /** Disconnect if subscribe failed */ - pClient->disconnect(); - return false; - } - } - } - - else{ + + } else { Serial.println("DEAD service not found."); } - + pSvc = pClient->getService("BAAD"); if(pSvc) { /** make sure it's not null */ pChr = pSvc->getCharacteristic("F00D"); - } - if(pChr) { /** make sure it's not null */ - if(pChr->canRead()) { - Serial.print(pChr->getUUID().toString().c_str()); - Serial.print(" Value: "); - Serial.println(pChr->readValue().c_str()); - } - - pDsc = pChr->getDescriptor(NimBLEUUID("C01D")); - if(pDsc) { /** make sure it's not null */ - Serial.print("Descriptor: "); - Serial.print(pDsc->getUUID().toString().c_str()); - Serial.print(" Value: "); - Serial.println(pDsc->readValue().c_str()); - } - - if(pChr->canWrite()) { - if(pChr->writeValue("No tip!")) { - Serial.print("Wrote new value to: "); - Serial.println(pChr->getUUID().toString().c_str()); - } - else { - /** Disconnect if write failed */ - pClient->disconnect(); - return false; - } - + if(pChr) { /** make sure it's not null */ if(pChr->canRead()) { - Serial.print("The value of: "); Serial.print(pChr->getUUID().toString().c_str()); - Serial.print(" is now: "); + Serial.print(" Value: "); Serial.println(pChr->readValue().c_str()); } - } - - /** registerForNotify() has been deprecated and replaced with subscribe() / unsubscribe(). - * Subscribe parameter defaults are: notifications=true, notifyCallback=nullptr, response=false. - * Unsubscribe parameter defaults are: response=false. - */ - if(pChr->canNotify()) { - //if(!pChr->registerForNotify(notifyCB)) { - if(!pChr->subscribe(true, notifyCB)) { - /** Disconnect if subscribe failed */ - pClient->disconnect(); - return false; - } - } - else if(pChr->canIndicate()) { - /** Send false as first argument to subscribe to indications instead of notifications */ - //if(!pChr->registerForNotify(notifyCB, false)) { - if(!pChr->subscribe(false, notifyCB)) { - /** Disconnect if subscribe failed */ - pClient->disconnect(); - return false; - } - } - } - else{ + pDsc = pChr->getDescriptor(NimBLEUUID("C01D")); + if(pDsc) { /** make sure it's not null */ + Serial.print("Descriptor: "); + Serial.print(pDsc->getUUID().toString().c_str()); + Serial.print(" Value: "); + Serial.println(pDsc->readValue().c_str()); + } + + if(pChr->canWrite()) { + if(pChr->writeValue("No tip!")) { + Serial.print("Wrote new value to: "); + Serial.println(pChr->getUUID().toString().c_str()); + } + else { + /** Disconnect if write failed */ + pClient->disconnect(); + return false; + } + + if(pChr->canRead()) { + Serial.print("The value of: "); + Serial.print(pChr->getUUID().toString().c_str()); + Serial.print(" is now: "); + Serial.println(pChr->readValue().c_str()); + } + } + + /** registerForNotify() has been deprecated and replaced with subscribe() / unsubscribe(). + * Subscribe parameter defaults are: notifications=true, notifyCallback=nullptr, response=false. + * Unsubscribe parameter defaults are: response=false. + */ + if(pChr->canNotify()) { + //if(!pChr->registerForNotify(notifyCB)) { + if(!pChr->subscribe(true, notifyCB)) { + /** Disconnect if subscribe failed */ + pClient->disconnect(); + return false; + } + } + else if(pChr->canIndicate()) { + /** Send false as first argument to subscribe to indications instead of notifications */ + //if(!pChr->registerForNotify(notifyCB, false)) { + if(!pChr->subscribe(false, notifyCB)) { + /** Disconnect if subscribe failed */ + pClient->disconnect(); + return false; + } + } + } + + } else { Serial.println("BAAD service not found."); } - + Serial.println("Done with this device!"); return true; } @@ -331,7 +329,7 @@ void setup (){ Serial.println("Starting NimBLE Client"); /** Initialize NimBLE, no device name spcified as we are not advertising */ NimBLEDevice::init(""); - + /** Set the IO capabilities of the device, each option will trigger a different pairing method. * BLE_HS_IO_KEYBOARD_ONLY - Passkey pairing * BLE_HS_IO_DISPLAY_YESNO - Numeric comparison pairing @@ -339,37 +337,37 @@ void setup (){ */ //NimBLEDevice::setSecurityIOCap(BLE_HS_IO_KEYBOARD_ONLY); // use passkey //NimBLEDevice::setSecurityIOCap(BLE_HS_IO_DISPLAY_YESNO); //use numeric comparison - + /** 2 different ways to set security - both calls achieve the same result. * no bonding, no man in the middle protection, secure connections. - * - * These are the default values, only shown here for demonstration. - */ - //NimBLEDevice::setSecurityAuth(false, false, true); + * + * These are the default values, only shown here for demonstration. + */ + //NimBLEDevice::setSecurityAuth(false, false, true); NimBLEDevice::setSecurityAuth(/*BLE_SM_PAIR_AUTHREQ_BOND | BLE_SM_PAIR_AUTHREQ_MITM |*/ BLE_SM_PAIR_AUTHREQ_SC); - + /** Optional: set the transmit power, default is 3db */ NimBLEDevice::setPower(ESP_PWR_LVL_P9); /** +9db */ - + /** Optional: set any devices you don't want to get advertisments from */ - // NimBLEDevice::addIgnored(NimBLEAddress ("aa:bb:cc:dd:ee:ff")); - - /** create new scan */ - NimBLEScan* pScan = NimBLEDevice::getScan(); - + // NimBLEDevice::addIgnored(NimBLEAddress ("aa:bb:cc:dd:ee:ff")); + + /** create new scan */ + NimBLEScan* pScan = NimBLEDevice::getScan(); + /** create a callback that gets called when advertisers are found */ pScan->setAdvertisedDeviceCallbacks(new AdvertisedDeviceCallbacks()); - + /** Set scan interval (how often) and window (how long) in milliseconds */ pScan->setInterval(45); pScan->setWindow(15); - + /** Active scan will gather scan response data from advertisers * but will use more energy from both devices */ pScan->setActiveScan(true); /** Start scanning for advertisers for the scan time specified (in seconds) 0 = forever - * Optional callback for when scanning stops. + * Optional callback for when scanning stops. */ pScan->start(scanTime, scanEndedCB); } @@ -380,15 +378,15 @@ void loop (){ while(!doConnect){ delay(1); } - + doConnect = false; - + /** Found a device we want to connect to, do it now */ if(connectToServer()) { Serial.println("Success! we should now be getting notifications, scanning for more!"); } else { Serial.println("Failed to connect, starting scan"); } - + NimBLEDevice::getScan()->start(scanTime,scanEndedCB); } diff --git a/lib/libesp32/NimBLE-Arduino/library.properties b/lib/libesp32/NimBLE-Arduino/library.properties index 156c098c0..7f2508333 100644 --- a/lib/libesp32/NimBLE-Arduino/library.properties +++ b/lib/libesp32/NimBLE-Arduino/library.properties @@ -1,5 +1,5 @@ name=NimBLE-Arduino -version=1.0.2 +version=1.1.0 author=h2zero maintainer=h2zero sentence=Bluetooth low energy (BLE) library for arduino-esp32 based on NimBLE. diff --git a/lib/libesp32/NimBLE-Arduino/src/NimBLEClient.cpp b/lib/libesp32/NimBLE-Arduino/src/NimBLEClient.cpp index ddd3abecc..ddeb1de70 100644 --- a/lib/libesp32/NimBLE-Arduino/src/NimBLEClient.cpp +++ b/lib/libesp32/NimBLE-Arduino/src/NimBLEClient.cpp @@ -100,9 +100,10 @@ NimBLEClient::~NimBLEClient() { * be called to reset the host in the case of a stalled controller. */ void NimBLEClient::dcTimerCb(ble_npl_event *event) { - NimBLEClient *pClient = (NimBLEClient*)event->arg; + /* NimBLEClient *pClient = (NimBLEClient*)event->arg; NIMBLE_LOGC(LOG_TAG, "Timed out disconnecting from %s - resetting host", std::string(pClient->getPeerAddress()).c_str()); + */ ble_hs_sched_reset(BLE_HS_ECONTROLLER); } @@ -182,25 +183,30 @@ bool NimBLEClient::connect(const NimBLEAddress &address, bool deleteAttibutes) { return false; } - if(isConnected() || m_pTaskData != nullptr) { + if(isConnected() || m_connEstablished || m_pTaskData != nullptr) { NIMBLE_LOGE(LOG_TAG, "Client busy, connected to %s, id=%d", std::string(m_peerAddress).c_str(), getConnId()); return false; } + ble_addr_t peerAddr_t; + memcpy(&peerAddr_t.val, address.getNative(),6); + peerAddr_t.type = address.getType(); + if(ble_gap_conn_find_by_addr(&peerAddr_t, NULL) == 0) { + NIMBLE_LOGE(LOG_TAG, "A connection to %s already exists", + address.toString().c_str()); + return false; + } + if(address == NimBLEAddress("")) { NIMBLE_LOGE(LOG_TAG, "Invalid peer address;(NULL)"); return false; - } else if(m_peerAddress != address) { + } else { m_peerAddress = address; } - ble_addr_t peerAddr_t; - memcpy(&peerAddr_t.val, m_peerAddress.getNative(),6); - peerAddr_t.type = m_peerAddress.getType(); - - ble_task_data_t taskData = {this, xTaskGetCurrentTaskHandle(), 0, nullptr}; + m_pTaskData = &taskData; int rc = 0; /* Try to connect the the advertiser. Allow 30 seconds (30000 ms) for @@ -213,13 +219,12 @@ bool NimBLEClient::connect(const NimBLEAddress &address, bool deleteAttibutes) { NimBLEClient::handleGapEvent, this); switch (rc) { case 0: - m_pTaskData = &taskData; break; case BLE_HS_EBUSY: // Scan was still running, stop it and try again if (!NimBLEDevice::getScan()->stop()) { - return false; + rc = BLE_HS_EUNKNOWN; } break; @@ -227,7 +232,7 @@ bool NimBLEClient::connect(const NimBLEAddress &address, bool deleteAttibutes) { // A connection to this device already exists, do not connect twice. NIMBLE_LOGE(LOG_TAG, "Already connected to device; addr=%s", std::string(m_peerAddress).c_str()); - return false; + break; case BLE_HS_EALREADY: // Already attemting to connect to this device, cancel the previous @@ -235,17 +240,22 @@ bool NimBLEClient::connect(const NimBLEAddress &address, bool deleteAttibutes) { NIMBLE_LOGE(LOG_TAG, "Already attempting to connect to %s - cancelling", std::string(m_peerAddress).c_str()); ble_gap_conn_cancel(); - return false; + break; default: NIMBLE_LOGE(LOG_TAG, "Failed to connect to %s, rc=%d; %s", std::string(m_peerAddress).c_str(), rc, NimBLEUtils::returnCodeToString(rc)); - return false; + break; } } while (rc == BLE_HS_EBUSY); + if(rc != 0) { + m_pTaskData = nullptr; + return false; + } + // Wait for the connect timeout time +1 second for the connection to complete if(ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(m_connectTimeout + 1000)) == pdFALSE) { m_pTaskData = nullptr; @@ -255,7 +265,7 @@ bool NimBLEClient::connect(const NimBLEAddress &address, bool deleteAttibutes) { disconnect(); } else { // workaround; if the controller doesn't cancel the connection - // at the timeout cancel it here. + // at the timeout, cancel it here. NIMBLE_LOGE(LOG_TAG, "Connect timeout - cancelling"); ble_gap_conn_cancel(); } @@ -269,7 +279,7 @@ bool NimBLEClient::connect(const NimBLEAddress &address, bool deleteAttibutes) { // If the failure was not a result of a disconnection // make sure we disconnect now to avoid dangling connections if(isConnected()) { - ble_gap_terminate(m_conn_id, BLE_ERR_REM_USER_CONN_TERM); + disconnect(); } return false; } else { @@ -325,26 +335,35 @@ bool NimBLEClient::secureConnection() { */ int NimBLEClient::disconnect(uint8_t reason) { NIMBLE_LOGD(LOG_TAG, ">> disconnect()"); - int rc = 0; - if(isConnected()){ - rc = ble_gap_terminate(m_conn_id, reason); - if (rc == 0) { - ble_addr_t peerAddr_t; - memcpy(&peerAddr_t.val, m_peerAddress.getNative(),6); - peerAddr_t.type = m_peerAddress.getType(); + if(isConnected()) { + // If the timer was already started, ignore this call. + if(ble_npl_callout_is_active(&m_dcTimer)) { + NIMBLE_LOGI(LOG_TAG, "Already disconnecting, timer started"); + return BLE_HS_EALREADY; + } - // Set the disconnect timeout to the supervison timeout time + 1 second - // In case the event triggers shortly after the supervision timeout. - // We don't want to prematurely reset the host. - ble_gap_conn_desc desc; - if(ble_gap_conn_find_by_addr(&peerAddr_t, &desc) == 0){ - ble_npl_time_t ticks; - ble_npl_time_ms_to_ticks((desc.supervision_timeout + 100) * 10, &ticks); - ble_npl_callout_reset(&m_dcTimer, ticks); - NIMBLE_LOGD(LOG_TAG, "DC TIMEOUT = %dms", (desc.supervision_timeout + 100) * 10); + ble_gap_conn_desc desc; + if(ble_gap_conn_find(m_conn_id, &desc) != 0){ + NIMBLE_LOGI(LOG_TAG, "Connection ID not found"); + return BLE_HS_EALREADY; + } + + // We use a timer to detect a controller error in the event that it does + // not inform the stack when disconnection is complete. + // This is a common error in certain esp-idf versions. + // The disconnect timeout time is the supervison timeout time + 1 second. + // In the case that the event happenss shortly after the supervision timeout + // we don't want to prematurely reset the host. + ble_npl_time_t ticks; + ble_npl_time_ms_to_ticks((desc.supervision_timeout + 100) * 10, &ticks); + ble_npl_callout_reset(&m_dcTimer, ticks); + + rc = ble_gap_terminate(m_conn_id, reason); + if (rc != 0) { + if(rc != BLE_HS_EALREADY) { + ble_npl_callout_stop(&m_dcTimer); } - } else if (rc != BLE_HS_EALREADY) { NIMBLE_LOGE(LOG_TAG, "ble_gap_terminate failed: rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc)); } @@ -359,12 +378,12 @@ int NimBLEClient::disconnect(uint8_t reason) { /** * @brief Set the connection paramaters to use when connecting to a server. - * @param [in] minInterval minimum connection interval in 0.625ms units. - * @param [in] maxInterval maximum connection interval in 0.625ms units. - * @param [in] latency number of packets allowed to skip (extends max interval) - * @param [in] timeout the timeout time in 10ms units before disconnecting - * @param [in] scanInterval the scan interval to use when attempting to connect in 0.625ms units. - * @param [in] scanWindow the scan window to use when attempting to connect in 0.625ms units. + * @param [in] minInterval The minimum connection interval in 1.25ms units. + * @param [in] maxInterval The maximum connection interval in 1.25ms units. + * @param [in] latency The number of packets allowed to skip (extends max interval). + * @param [in] timeout The timeout time in 10ms units before disconnecting. + * @param [in] scanInterval The scan interval to use when attempting to connect in 0.625ms units. + * @param [in] scanWindow The scan window to use when attempting to connect in 0.625ms units. */ void NimBLEClient::setConnectionParams(uint16_t minInterval, uint16_t maxInterval, uint16_t latency, uint16_t timeout, @@ -391,10 +410,10 @@ void NimBLEClient::setConnectionParams(uint16_t minInterval, uint16_t maxInterva /** * @brief Update the connection parameters: * * Can only be used after a connection has been established. - * @param [in] minInterval minimum connection interval in 0.625ms units. - * @param [in] maxInterval maximum connection interval in 0.625ms units. - * @param [in] latency number of packets allowed to skip (extends max interval) - * @param [in] timeout the timeout time in 10ms units before disconnecting + * @param [in] minInterval The minimum connection interval in 1.25ms units. + * @param [in] maxInterval The maximum connection interval in 1.25ms units. + * @param [in] latency The number of packets allowed to skip (extends max interval). + * @param [in] timeout The timeout time in 10ms units before disconnecting. */ void NimBLEClient::updateConnParams(uint16_t minInterval, uint16_t maxInterval, uint16_t latency, uint16_t timeout) @@ -797,14 +816,15 @@ uint16_t NimBLEClient::getMTU() { break; } - client->m_conn_id = BLE_HS_CONN_HANDLE_NONE; - // Stop the disconnect timer since we are now disconnected. ble_npl_callout_stop(&client->m_dcTimer); // Remove the device from ignore list so we will scan it again NimBLEDevice::removeIgnored(client->m_peerAddress); + // No longer connected, clear the connection ID. + client->m_conn_id = BLE_HS_CONN_HANDLE_NONE; + // If we received a connected event but did not get established (no PDU) // then a disconnect event will be sent but we should not send it to the // app for processing. Instead we will ensure the task is released diff --git a/lib/libesp32/NimBLE-Arduino/src/NimBLEHIDDevice.cpp b/lib/libesp32/NimBLE-Arduino/src/NimBLEHIDDevice.cpp index 39f07dede..a06b75360 100644 --- a/lib/libesp32/NimBLE-Arduino/src/NimBLEHIDDevice.cpp +++ b/lib/libesp32/NimBLE-Arduino/src/NimBLEHIDDevice.cpp @@ -20,6 +20,10 @@ #include "NimBLEHIDDevice.h" #include "NimBLE2904.h" +/** + * @brief Construct a default NimBLEHIDDevice object. + * @param [in] server A pointer to the server instance this HID Device will use. + */ NimBLEHIDDevice::NimBLEHIDDevice(NimBLEServer* server) { /* * Here we create mandatory services described in bluetooth specification @@ -61,15 +65,18 @@ NimBLEHIDDevice::NimBLEHIDDevice(NimBLEServer* server) { NimBLEHIDDevice::~NimBLEHIDDevice() { } -/* - * @brief +/** + * @brief Set the report map data formatting information. + * @param [in] map A pointer to an array with the values to set. + * @param [in] size The number of values in the array. */ void NimBLEHIDDevice::reportMap(uint8_t* map, uint16_t size) { m_reportMapCharacteristic->setValue(map, size); } -/* - * @brief This function suppose to be called at the end, when we have created all characteristics we need to build HID service +/** + * @brief Start the HID device services.\n + * This function called when all the services have been created. */ void NimBLEHIDDevice::startServices() { m_deviceInfoService->start(); @@ -77,41 +84,47 @@ void NimBLEHIDDevice::startServices() { m_batteryService->start(); } -/* - * @brief Create manufacturer characteristic (this characteristic is optional) +/** + * @brief Create a manufacturer characteristic (this characteristic is optional). */ NimBLECharacteristic* NimBLEHIDDevice::manufacturer() { m_manufacturerCharacteristic = m_deviceInfoService->createCharacteristic((uint16_t) 0x2a29, NIMBLE_PROPERTY::READ); return m_manufacturerCharacteristic; } -/* +/** * @brief Set manufacturer name - * @param [in] name manufacturer name + * @param [in] name The manufacturer name of this HID device. */ void NimBLEHIDDevice::manufacturer(std::string name) { m_manufacturerCharacteristic->setValue(name); } -/* - * @brief +/** + * @brief Sets the Plug n Play characterisc value. + * @param [in] sig The vendor ID source number. + * @param [in[ vid The vendor ID number. + * @param [in] pid The product ID number. + * @param [in] version The produce version number. */ void NimBLEHIDDevice::pnp(uint8_t sig, uint16_t vid, uint16_t pid, uint16_t version) { uint8_t pnp[] = { sig, (uint8_t) (vid >> 8), (uint8_t) vid, (uint8_t) (pid >> 8), (uint8_t) pid, (uint8_t) (version >> 8), (uint8_t) version }; m_pnpCharacteristic->setValue(pnp, sizeof(pnp)); } -/* - * @brief +/** + * @brief Sets the HID Information characteristic value. + * @param [in] country The country code for the device. + * @param [in] flags The HID Class Specification release number to use. */ void NimBLEHIDDevice::hidInfo(uint8_t country, uint8_t flags) { uint8_t info[] = { 0x11, 0x1, country, flags }; m_hidInfoCharacteristic->setValue(info, sizeof(info)); } -/* - * @brief Create input report characteristic that need to be saved as new characteristic object so can be further used - * @param [in] reportID input report ID, the same as in report map for input object related to created characteristic +/** + * @brief Create input report characteristic + * @param [in] reportID input report ID, the same as in report map for input object related to the characteristic * @return pointer to new input report characteristic */ NimBLECharacteristic* NimBLEHIDDevice::inputReport(uint8_t reportID) { @@ -124,9 +137,9 @@ NimBLECharacteristic* NimBLEHIDDevice::inputReport(uint8_t reportID) { return inputReportCharacteristic; } -/* - * @brief Create output report characteristic that need to be saved as new characteristic object so can be further used - * @param [in] reportID Output report ID, the same as in report map for output object related to created characteristic +/** + * @brief Create output report characteristic + * @param [in] reportID Output report ID, the same as in report map for output object related to the characteristic * @return Pointer to new output report characteristic */ NimBLECharacteristic* NimBLEHIDDevice::outputReport(uint8_t reportID) { @@ -139,9 +152,9 @@ NimBLECharacteristic* NimBLEHIDDevice::outputReport(uint8_t reportID) { return outputReportCharacteristic; } -/* - * @brief Create feature report characteristic that need to be saved as new characteristic object so can be further used - * @param [in] reportID Feature report ID, the same as in report map for feature object related to created characteristic +/** + * @brief Create feature report characteristic. + * @param [in] reportID Feature report ID, the same as in report map for feature object related to the characteristic * @return Pointer to new feature report characteristic */ NimBLECharacteristic* NimBLEHIDDevice::featureReport(uint8_t reportID) { @@ -154,34 +167,38 @@ NimBLECharacteristic* NimBLEHIDDevice::featureReport(uint8_t reportID) { return featureReportCharacteristic; } -/* - * @brief +/** + * @brief Creates a keyboard boot input report characteristic */ NimBLECharacteristic* NimBLEHIDDevice::bootInput() { return m_hidService->createCharacteristic((uint16_t) 0x2a22, NIMBLE_PROPERTY::NOTIFY); } -/* - * @brief +/** + * @brief Create a keyboard boot output report characteristic */ NimBLECharacteristic* NimBLEHIDDevice::bootOutput() { return m_hidService->createCharacteristic((uint16_t) 0x2a32, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::WRITE_NR); } -/* - * @brief +/** + * @brief Returns a pointer to the HID control point characteristic. */ NimBLECharacteristic* NimBLEHIDDevice::hidControl() { return m_hidControlCharacteristic; } -/* - * @brief +/** + * @brief Returns a pointer to the protocol mode characteristic. */ NimBLECharacteristic* NimBLEHIDDevice::protocolMode() { return m_protocolModeCharacteristic; } +/** + * @brief Set the battery level characteristic value. + * @param [in] level The battery level value. + */ void NimBLEHIDDevice::setBatteryLevel(uint8_t level) { m_batteryLevelCharacteristic->setValue(&level, 1); } @@ -208,22 +225,23 @@ BLECharacteristic* BLEHIDDevice::hidInfo() { return m_hidInfoCharacteristic; } */ -/* - * @brief + +/** + * @brief Returns a pointer to the device information service. */ NimBLEService* NimBLEHIDDevice::deviceInfo() { return m_deviceInfoService; } -/* - * @brief +/** + * @brief Returns a pointer to the HID service. */ NimBLEService* NimBLEHIDDevice::hidService() { return m_hidService; } -/* - * @brief +/** + * @brief @brief Returns a pointer to the battery service. */ NimBLEService* NimBLEHIDDevice::batteryService() { return m_batteryService; diff --git a/lib/libesp32/NimBLE-Arduino/src/NimBLEHIDDevice.h b/lib/libesp32/NimBLE-Arduino/src/NimBLEHIDDevice.h index 3e7a6f759..6ed7c2bde 100644 --- a/lib/libesp32/NimBLE-Arduino/src/NimBLEHIDDevice.h +++ b/lib/libesp32/NimBLE-Arduino/src/NimBLEHIDDevice.h @@ -36,6 +36,10 @@ #define HID_DIGITAL_PEN 0x03C7 #define HID_BARCODE 0x03C8 + +/** + * @brief A model of a %BLE Human Interface Device. + */ class NimBLEHIDDevice { public: NimBLEHIDDevice(NimBLEServer*); diff --git a/lib/libesp32/NimBLE-Arduino/src/NimBLEServer.cpp b/lib/libesp32/NimBLE-Arduino/src/NimBLEServer.cpp index 655511aea..fd5d8266b 100644 --- a/lib/libesp32/NimBLE-Arduino/src/NimBLEServer.cpp +++ b/lib/libesp32/NimBLE-Arduino/src/NimBLEServer.cpp @@ -621,7 +621,13 @@ uint16_t NimBLEServer::getPeerMTU(uint16_t conn_id) { /** - * Update connection parameters can be called only after connection has been established + * @brief Request an Update the connection parameters: + * * Can only be used after a connection has been established. + * @param [in] conn_handle The connection handle of the peer to send the request to. + * @param [in] minInterval The minimum connection interval in 1.25ms units. + * @param [in] maxInterval The maximum connection interval in 1.25ms units. + * @param [in] latency The number of packets allowed to skip (extends max interval). + * @param [in] timeout The timeout time in 10ms units before disconnecting. */ void NimBLEServer::updateConnParams(uint16_t conn_handle, uint16_t minInterval, uint16_t maxInterval,