diff --git a/lib/libesp32_div/esp-nimble-cpp/CMakeLists.txt b/lib/libesp32_div/esp-nimble-cpp/CMakeLists.txt index 90c22bd19..4bed65b11 100644 --- a/lib/libesp32_div/esp-nimble-cpp/CMakeLists.txt +++ b/lib/libesp32_div/esp-nimble-cpp/CMakeLists.txt @@ -31,6 +31,7 @@ idf_component_register( "esp32c2" "esp32c3" "esp32c6" + "esp32h2" INCLUDE_DIRS "src" SRCS @@ -58,6 +59,7 @@ idf_component_register( REQUIRES bt nvs_flash + driver PRIV_REQUIRES ${ESP_NIMBLE_PRIV_REQUIRES} ) diff --git a/lib/libesp32_div/esp-nimble-cpp/Kconfig b/lib/libesp32_div/esp-nimble-cpp/Kconfig index 730f8cd96..0878176a8 100644 --- a/lib/libesp32_div/esp-nimble-cpp/Kconfig +++ b/lib/libesp32_div/esp-nimble-cpp/Kconfig @@ -33,7 +33,7 @@ config NIMBLE_CPP_ENABLE_RETURN_CODE_TEXT Enabling this option will display return code values as text messages in the debug log. This will use approximately 8kB of flash memory. - + config NIMBLE_CPP_ENABLE_GAP_EVENT_CODE_TEXT bool "Show NimBLE gap events as text in debug log." default "n" @@ -47,7 +47,7 @@ config NIMBLE_CPP_ENABLE_ADVERTISEMENT_TYPE_TEXT default "n" help Enabling this option will display advertisment types recieved - while scanning as text messages in the debug log. + while scanning as text messages in the debug log. This will use approximately 250 bytes of flash memory. config NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED @@ -68,5 +68,12 @@ config NIMBLE_CPP_ATT_VALUE_INIT_LENGTH when the constructor is called. This is also the size used when a remote characteristic or descriptor is constructed before a value is read/notifed. Increasing this will reduce reallocations but increase memory footprint. - + +config NIMBLE_CPP_DEBUG_ASSERT_ENABLED + bool "Enable debug asserts." + default "n" + help + Enabling this option will add debug asserts to the NimBLE CPP library. + This will use approximately 1kB of flash memory. + endmenu diff --git a/lib/libesp32_div/esp-nimble-cpp/docs/Improvements_and_updates.md b/lib/libesp32_div/esp-nimble-cpp/docs/Improvements_and_updates.md index 220ed5c28..a7504d5fb 100644 --- a/lib/libesp32_div/esp-nimble-cpp/docs/Improvements_and_updates.md +++ b/lib/libesp32_div/esp-nimble-cpp/docs/Improvements_and_updates.md @@ -66,7 +66,7 @@ If false the service is only removed from visibility by clients. The pointers to # Advertising `NimBLEAdvertising::start` -Now takes 2 optional parameters, the first is the duration to advertise for (in seconds), the second is a callback that is invoked when advertising ends and takes a pointer to a `NimBLEAdvertising` object (similar to the `NimBLEScan::start` API). +Now takes 2 optional parameters, the first is the duration to advertise for (in milliseconds), the second is a callback that is invoked when advertising ends and takes a pointer to a `NimBLEAdvertising` object (similar to the `NimBLEScan::start` API). This provides an opportunity to update the advertisement data if desired. diff --git a/lib/libesp32_div/esp-nimble-cpp/docs/Migration_guide.md b/lib/libesp32_div/esp-nimble-cpp/docs/Migration_guide.md index d6362e2b2..d1fcee8ad 100644 --- a/lib/libesp32_div/esp-nimble-cpp/docs/Migration_guide.md +++ b/lib/libesp32_div/esp-nimble-cpp/docs/Migration_guide.md @@ -255,7 +255,7 @@ Calling `NimBLEAdvertising::setAdvertisementData` will entirely replace any data > BLEAdvertising::start (NimBLEAdvertising::start) -Now takes 2 optional parameters, the first is the duration to advertise for (in seconds), the second is a callback that is invoked when advertising ends and takes a pointer to a `NimBLEAdvertising` object (similar to the `NimBLEScan::start` API). +Now takes 2 optional parameters, the first is the duration to advertise for (in milliseconds), the second is a callback that is invoked when advertising ends and takes a pointer to a `NimBLEAdvertising` object (similar to the `NimBLEScan::start` API). This provides an opportunity to update the advertisement data if desired.
@@ -383,18 +383,23 @@ The security callback methods are now incorporated in the `NimBLEServerCallbacks The callback methods are: -> `bool onConfirmPIN(uint32_t pin)` +> `bool onConfirmPIN(const NimBLEConnInfo& connInfo, uint32_t pin)` -Receives the pin when using numeric comparison authentication, `return true;` to accept. +Receives the pin when using numeric comparison authentication. +Call `NimBLEDevice::injectConfirmPIN(connInfo, true);` to accept or `NimBLEDevice::injectConfirmPIN(connInfo, false);` to reject.
-> `uint32_t onPassKeyRequest()` +> `void onPassKeyEntry(const NimBLEConnInfo& connInfo)` -For server callback; return the passkey expected from the client. -For client callback; return the passkey to send to the server. +Client callback; client should respond with the passkey (pin) by calling `NimBLEDevice::injectPassKey(connInfo, 123456);`
-> `void onAuthenticationComplete(NimBLEConnInfo& connInfo)` +> `uint32_t onPassKeyDisplay()` + +Server callback; should return the passkey (pin) expected from the client. +
+ +> `void onAuthenticationComplete(const NimBLEConnInfo& connInfo)` Authentication complete, success or failed information is available from the `NimBLEConnInfo` methods.
diff --git a/lib/libesp32_div/esp-nimble-cpp/examples/Advanced/NimBLE_Client/main/main.cpp b/lib/libesp32_div/esp-nimble-cpp/examples/Advanced/NimBLE_Client/main/main.cpp index e30bd646b..fe76dd0d9 100644 --- a/lib/libesp32_div/esp-nimble-cpp/examples/Advanced/NimBLE_Client/main/main.cpp +++ b/lib/libesp32_div/esp-nimble-cpp/examples/Advanced/NimBLE_Client/main/main.cpp @@ -39,20 +39,22 @@ class ClientCallbacks : public NimBLEClientCallbacks { /********************* Security handled here ********************** ****** Note: these are the same return values as defaults ********/ - uint32_t onPassKeyRequest(){ - printf("Client Passkey Request\n"); - /** return the passkey to send to the server */ - return 123456; - } + void onPassKeyEntry(const NimBLEConnInfo& connInfo){ + printf("Server Passkey Entry\n"); + /** This should prompt the user to enter the passkey displayed + * on the peer device. + */ + NimBLEDevice::injectPassKey(connInfo, 123456); + }; - bool onConfirmPIN(uint32_t pass_key){ - printf("The passkey YES/NO number: %" PRIu32"\n", pass_key); - /** Return false if passkeys don't match. */ - return true; - } + void onConfirmPIN(const NimBLEConnInfo& connInfo, uint32_t pass_key){ + printf("The passkey YES/NO number: %" PRIu32 "\n", pass_key); + /** Inject false if passkeys don't match. */ + NimBLEDevice::injectConfirmPIN(connInfo, true); + }; /** Pairing process complete, we can check the results in connInfo */ - void onAuthenticationComplete(NimBLEConnInfo& connInfo){ + void onAuthenticationComplete(const NimBLEConnInfo& connInfo){ if(!connInfo.isEncrypted()) { printf("Encrypt connection failed - disconnecting\n"); /** Find the client with the connection handle provided in desc */ @@ -146,8 +148,8 @@ bool connectToServer() { * Min interval: 12 * 1.25ms = 15, Max interval: 12 * 1.25ms = 15, 0 latency, 12 * 10ms = 120ms timeout */ pClient->setConnectionParams(6,6,0,15); - /** Set how long we are willing to wait for the connection to complete (seconds), default is 30. */ - pClient->setConnectTimeout(5); + /** Set how long we are willing to wait for the connection to complete (milliseconds), default is 30000. */ + pClient->setConnectTimeout(5 * 1000); if (!pClient->connect(advDevice)) { @@ -358,7 +360,7 @@ void app_main (void){ * but will use more energy from both devices */ pScan->setActiveScan(true); - /** Start scanning for advertisers for the scan time specified (in seconds) 0 = forever + /** Start scanning for advertisers for the scan time specified (in milliseconds) 0 = forever * Optional callback for when scanning stops. */ pScan->start(scanTime); diff --git a/lib/libesp32_div/esp-nimble-cpp/examples/Advanced/NimBLE_Server/main/main.cpp b/lib/libesp32_div/esp-nimble-cpp/examples/Advanced/NimBLE_Server/main/main.cpp index 8bf92e26a..3effb54f2 100644 --- a/lib/libesp32_div/esp-nimble-cpp/examples/Advanced/NimBLE_Server/main/main.cpp +++ b/lib/libesp32_div/esp-nimble-cpp/examples/Advanced/NimBLE_Server/main/main.cpp @@ -44,21 +44,21 @@ class ServerCallbacks: public NimBLEServerCallbacks { /********************* Security handled here ********************** ****** Note: these are the same return values as defaults ********/ - uint32_t onPassKeyRequest(){ - printf("Server Passkey Request\n"); + uint32_t onPassKeyDisplay(){ + printf("Server Passkey Display\n"); /** This should return a random 6 digit number for security * or make your own static passkey as done here. */ return 123456; }; - bool onConfirmPIN(uint32_t pass_key){ - printf("The passkey YES/NO number: %" PRIu32"\n", pass_key); - /** Return false if passkeys don't match. */ - return true; + void onConfirmPIN(const NimBLEConnInfo& connInfo, uint32_t pass_key){ + printf("The passkey YES/NO number: %" PRIu32 "\n", pass_key); + /** Inject false if passkeys don't match. */ + NimBLEDevice::injectConfirmPIN(connInfo, true); }; - void onAuthenticationComplete(NimBLEConnInfo& connInfo){ + void onAuthenticationComplete(const NimBLEConnInfo& connInfo){ /** Check that encryption was successful, if not we disconnect the client */ if(!connInfo.isEncrypted()) { NimBLEDevice::getServer()->disconnect(connInfo.getConnHandle()); diff --git a/lib/libesp32_div/esp-nimble-cpp/examples/Bluetooth_5/NimBLE_extended_client/main/main.cpp b/lib/libesp32_div/esp-nimble-cpp/examples/Bluetooth_5/NimBLE_extended_client/main/main.cpp index c9013d9c7..3572e59ef 100644 --- a/lib/libesp32_div/esp-nimble-cpp/examples/Bluetooth_5/NimBLE_extended_client/main/main.cpp +++ b/lib/libesp32_div/esp-nimble-cpp/examples/Bluetooth_5/NimBLE_extended_client/main/main.cpp @@ -154,7 +154,7 @@ void app_main (void) { */ pScan->setActiveScan(true); - /* Start scanning for advertisers for the scan time specified (in seconds) 0 = forever + /* Start scanning for advertisers for the scan time specified (in milliseconds) 0 = forever * Optional callback for when scanning stops. */ pScan->start(scanTime); diff --git a/lib/libesp32_div/esp-nimble-cpp/examples/NimBLE_active_passive_scan/NimBLE_active_passive_scan.ino b/lib/libesp32_div/esp-nimble-cpp/examples/NimBLE_active_passive_scan/NimBLE_active_passive_scan.ino index 9dceb4e67..e69de29bb 100644 --- a/lib/libesp32_div/esp-nimble-cpp/examples/NimBLE_active_passive_scan/NimBLE_active_passive_scan.ino +++ b/lib/libesp32_div/esp-nimble-cpp/examples/NimBLE_active_passive_scan/NimBLE_active_passive_scan.ino @@ -1,48 +0,0 @@ -/* - * NimBLE Scan active/passive switching demo - * - * Demonstrates the use of the scan callbacks while alternating between passive and active scanning. - */ - -#include "NimBLEDevice.h" -int scanTime = 5 * 1000; // In milliseconds, 0 = scan forever -BLEScan* pBLEScan; - -bool active = false; - -class scanCallbacks: public NimBLEScanCallbacks { - - void onDiscovered(NimBLEAdvertisedDevice* advertisedDevice) { - Serial.printf("Discovered Advertised Device: %s \n", advertisedDevice->toString().c_str()); - } - - void onResult(NimBLEAdvertisedDevice* advertisedDevice) { - Serial.printf("Advertised Device Result: %s \n", advertisedDevice->toString().c_str()); - } - - void onScanEnd(NimBLEScanResults results){ - Serial.println("Scan Ended"); - active = !active; - pBLEScan->setActiveScan(active); - Serial.printf("scan start, active = %u\n", active); - pBLEScan->start(scanTime); - } -}; - - - -void setup() { - Serial.begin(115200); - Serial.println("Scanning..."); - - NimBLEDevice::init(""); - pBLEScan = NimBLEDevice::getScan(); - pBLEScan->setScanCallbacks(new scanCallbacks()); - pBLEScan->setActiveScan(active); - pBLEScan->setInterval(100); - pBLEScan->setWindow(99); - pBLEScan->start(scanTime); -} - -void loop() { -} diff --git a/lib/libesp32_div/esp-nimble-cpp/examples/NimBLE_server_get_client_name/CMakeLists.txt b/lib/libesp32_div/esp-nimble-cpp/examples/NimBLE_server_get_client_name/CMakeLists.txt new file mode 100644 index 000000000..21c12dad3 --- /dev/null +++ b/lib/libesp32_div/esp-nimble-cpp/examples/NimBLE_server_get_client_name/CMakeLists.txt @@ -0,0 +1,7 @@ +# The following lines of boilerplate have to be in your project's +# CMakeLists in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.5) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +set(SUPPORTED_TARGETS esp32) +project(NimBLE_server_get_client_name) diff --git a/lib/libesp32_div/esp-nimble-cpp/examples/NimBLE_server_get_client_name/main/CMakeLists.txt b/lib/libesp32_div/esp-nimble-cpp/examples/NimBLE_server_get_client_name/main/CMakeLists.txt new file mode 100644 index 000000000..0a5a557ec --- /dev/null +++ b/lib/libesp32_div/esp-nimble-cpp/examples/NimBLE_server_get_client_name/main/CMakeLists.txt @@ -0,0 +1,4 @@ +set(COMPONENT_SRCS "main.cpp") +set(COMPONENT_ADD_INCLUDEDIRS ".") + +register_component() diff --git a/lib/libesp32_div/esp-nimble-cpp/examples/NimBLE_server_get_client_name/main/main.cpp b/lib/libesp32_div/esp-nimble-cpp/examples/NimBLE_server_get_client_name/main/main.cpp new file mode 100644 index 000000000..e255807f8 --- /dev/null +++ b/lib/libesp32_div/esp-nimble-cpp/examples/NimBLE_server_get_client_name/main/main.cpp @@ -0,0 +1,83 @@ +/** NimBLE_server_get_client_name + * + * Demonstrates 2 ways for the server to read the device name from the connected client. + * + * Created: on June 24 2024 + * Author: H2zero + * + */ + +#include + +// See the following for generating UUIDs: +// https://www.uuidgenerator.net/ + +#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b" +#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8" +#define ENC_CHARACTERISTIC_UUID "9551f35b-8d91-42e4-8f7e-1358dfe272dc" + +NimBLEServer* pServer; + +class ServerCallbacks : public NimBLEServerCallbacks { + // Same as before but now includes the name parameter + void onConnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo, std::string& name) override { + printf("Client address: %s Name: %s\n", connInfo.getAddress().toString().c_str(), name.c_str()); + } + + // Same as before but now includes the name parameter + void onAuthenticationComplete(const NimBLEConnInfo& connInfo, const std::string& name) override { + if (!connInfo.isEncrypted()) { + NimBLEDevice::getServer()->disconnect(connInfo.getConnHandle()); + printf("Encrypt connection failed - disconnecting client\n"); + return; + } + + printf("Encrypted Client address: %s Name: %s\n", connInfo.getAddress().toString().c_str(), name.c_str()); + } +}; + +extern "C" void app_main(void) { + printf("Starting BLE Server!\n"); + + NimBLEDevice::init("Connect to me!"); + NimBLEDevice::setSecurityAuth(true, false, true); // Enable bonding to see full name on phones. + + pServer = NimBLEDevice::createServer(); + NimBLEService* pService = pServer->createService(SERVICE_UUID); + NimBLECharacteristic* pCharacteristic = + pService->createCharacteristic(CHARACTERISTIC_UUID, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE); + pCharacteristic->setValue("Hello World says NimBLE!"); + + NimBLECharacteristic* pEncCharacteristic = pService->createCharacteristic( + ENC_CHARACTERISTIC_UUID, + (NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::READ_ENC | NIMBLE_PROPERTY::WRITE_ENC)); + pEncCharacteristic->setValue("Hello World says NimBLE Encrypted"); + + pService->start(); + + pServer->setCallbacks(new ServerCallbacks()); + pServer->getPeerNameOnConnect(true); // Setting this will enable the onConnect callback that provides the name. + + BLEAdvertising* pAdvertising = NimBLEDevice::getAdvertising(); + pAdvertising->addServiceUUID(SERVICE_UUID); + pAdvertising->setScanResponse(true); + + pAdvertising->start(); + printf("Advertising started, connect with your phone.\n"); + + while (true) { + auto clientCount = pServer->getConnectedCount(); + if (clientCount) { + printf("Connected clients:\n"); + for (auto i = 0; i < clientCount; ++i) { + NimBLEConnInfo peerInfo = pServer->getPeerInfo(i); + printf("Client address: %s Name: %s\n", peerInfo.getAddress().toString().c_str(), + // This function blocks until the name is retrieved, so cannot be used in callback functions. + pServer->getPeerName(peerInfo).c_str()); + + } + } + + vTaskDelay(pdMS_TO_TICKS(10000)); + } +} diff --git a/lib/libesp32_div/esp-nimble-cpp/examples/basic/BLE_client/main/main.cpp b/lib/libesp32_div/esp-nimble-cpp/examples/basic/BLE_client/main/main.cpp index 18308840e..cfb80e9bf 100644 --- a/lib/libesp32_div/esp-nimble-cpp/examples/basic/BLE_client/main/main.cpp +++ b/lib/libesp32_div/esp-nimble-cpp/examples/basic/BLE_client/main/main.cpp @@ -51,17 +51,28 @@ class MyClientCallback : public BLEClientCallbacks { } /***************** New - Security handled here ******************** ****** Note: these are the same return values as defaults ********/ - uint32_t onPassKeyRequest(){ - printf("Client PassKeyRequest\n"); - return 123456; - } - bool onConfirmPIN(uint32_t pass_key){ - printf("The passkey YES/NO number: %" PRIu32"\n", pass_key); - return true; - } + void onPassKeyEntry(const NimBLEConnInfo& connInfo){ + printf("Server Passkey Entry\n"); + /** This should prompt the user to enter the passkey displayed + * on the peer device. + */ + NimBLEDevice::injectPassKey(connInfo, 123456); + }; - void onAuthenticationComplete(BLEConnInfo& connInfo){ - printf("Starting BLE work!\n"); + void onConfirmPIN(const NimBLEConnInfo& connInfo, uint32_t pass_key){ + printf("The passkey YES/NO number: %" PRIu32 "\n", pass_key); + /** Inject false if passkeys don't match. */ + NimBLEDevice::injectConfirmPIN(connInfo, true); + }; + + /** Pairing process complete, we can check the results in connInfo */ + void onAuthenticationComplete(const NimBLEConnInfo& connInfo){ + if(!connInfo.isEncrypted()) { + printf("Encrypt connection failed - disconnecting\n"); + /** Find the client with the connection handle provided in desc */ + NimBLEDevice::getClientByID(connInfo.getConnHandle())->disconnect(); + return; + } } /*******************************************************************/ }; diff --git a/lib/libesp32_div/esp-nimble-cpp/examples/basic/BLE_notify/main/main.cpp b/lib/libesp32_div/esp-nimble-cpp/examples/basic/BLE_notify/main/main.cpp index e966f8b8e..b17f49aaa 100644 --- a/lib/libesp32_div/esp-nimble-cpp/examples/basic/BLE_notify/main/main.cpp +++ b/lib/libesp32_div/esp-nimble-cpp/examples/basic/BLE_notify/main/main.cpp @@ -57,19 +57,29 @@ class MyServerCallbacks: public BLEServerCallbacks { } /***************** New - Security handled here ******************** ****** Note: these are the same return values as defaults ********/ - uint32_t onPassKeyRequest(){ - printf("Server PassKeyRequest\n"); - return 123456; - } + uint32_t onPassKeyDisplay(){ + printf("Server Passkey Display\n"); + /** This should return a random 6 digit number for security + * or make your own static passkey as done here. + */ + return 123456; + }; - bool onConfirmPIN(uint32_t pass_key){ - printf("The passkey YES/NO number: %" PRIu32"\n", pass_key); - return true; - } + void onConfirmPIN(const NimBLEConnInfo& connInfo, uint32_t pass_key){ + printf("The passkey YES/NO number: %" PRIu32 "\n", pass_key); + /** Inject false if passkeys don't match. */ + NimBLEDevice::injectConfirmPIN(connInfo, true); + }; - void onAuthenticationComplete(BLEConnInfo& connInfo){ - printf("Starting BLE work!\n"); - } + void onAuthenticationComplete(const NimBLEConnInfo& connInfo){ + /** Check that encryption was successful, if not we disconnect the client */ + if(!connInfo.isEncrypted()) { + NimBLEDevice::getServer()->disconnect(connInfo.getConnHandle()); + printf("Encrypt connection failed - disconnecting client\n"); + return; + } + printf("Starting BLE work!"); + }; /*******************************************************************/ }; @@ -128,7 +138,6 @@ void app_main(void) { NIMBLE_PROPERTY::INDICATE ); - // https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.client_characteristic_configuration.xml // Create a BLE Descriptor /*************************************************** NOTE: DO NOT create a 2902 descriptor. diff --git a/lib/libesp32_div/esp-nimble-cpp/examples/basic/BLE_uart/main/main.cpp b/lib/libesp32_div/esp-nimble-cpp/examples/basic/BLE_uart/main/main.cpp index 3aa20fb94..18df6fa63 100644 --- a/lib/libesp32_div/esp-nimble-cpp/examples/basic/BLE_uart/main/main.cpp +++ b/lib/libesp32_div/esp-nimble-cpp/examples/basic/BLE_uart/main/main.cpp @@ -59,19 +59,29 @@ class MyServerCallbacks: public BLEServerCallbacks { } /***************** New - Security handled here ******************** ****** Note: these are the same return values as defaults ********/ - uint32_t onPassKeyRequest(){ - printf("Server PassKeyRequest\n"); - return 123456; - } + uint32_t onPassKeyDisplay(){ + printf("Server Passkey Display\n"); + /** This should return a random 6 digit number for security + * or make your own static passkey as done here. + */ + return 123456; + }; - bool onConfirmPIN(uint32_t pass_key){ - printf("The passkey YES/NO number: %" PRIu32"\n", pass_key); - return true; - } + void onConfirmPIN(const NimBLEConnInfo& connInfo, uint32_t pass_key){ + printf("The passkey YES/NO number: %" PRIu32 "\n", pass_key); + /** Inject false if passkeys don't match. */ + NimBLEDevice::injectConfirmPIN(connInfo, true); + }; - void onAuthenticationComplete(BLEConnInfo& connInfo){ - printf("Starting BLE work!\n"); - } + void onAuthenticationComplete(const NimBLEConnInfo& connInfo){ + /** Check that encryption was successful, if not we disconnect the client */ + if(!connInfo.isEncrypted()) { + NimBLEDevice::getServer()->disconnect(connInfo.getConnHandle()); + printf("Encrypt connection failed - disconnecting client\n"); + return; + } + printf("Starting BLE work!"); + }; /*******************************************************************/ }; diff --git a/lib/libesp32_div/esp-nimble-cpp/package.json b/lib/libesp32_div/esp-nimble-cpp/package.json new file mode 100644 index 000000000..1efe13e63 --- /dev/null +++ b/lib/libesp32_div/esp-nimble-cpp/package.json @@ -0,0 +1,17 @@ +{ + "name": "esp-nimble-cpp", + "version": "1.5.0", + "description": "NimBLE, BLE stack for the Espressif ESP32, ESP32-S and ESP32-C series of SoCs", + "keywords": [ + "BLE", + "espidf", + "arduino", + "espressif", + "esp32" + ], + "license": "LGPL-2.1-or-later", + "repository": { + "type": "git", + "url": "https://github.com/h2zero/esp-nimble-cpp" + } +} diff --git a/lib/libesp32_div/esp-nimble-cpp/src/NimBLE2904.cpp b/lib/libesp32_div/esp-nimble-cpp/src/NimBLE2904.cpp index b518d9b00..486fa5ef5 100644 --- a/lib/libesp32_div/esp-nimble-cpp/src/NimBLE2904.cpp +++ b/lib/libesp32_div/esp-nimble-cpp/src/NimBLE2904.cpp @@ -12,10 +12,6 @@ * Author: kolban */ -/* - * See also: - * https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.characteristic_presentation_format.xml - */ #include "nimconfig.h" #if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) diff --git a/lib/libesp32_div/esp-nimble-cpp/src/NimBLE2904.h b/lib/libesp32_div/esp-nimble-cpp/src/NimBLE2904.h index 29dde51e8..52ae2d3db 100644 --- a/lib/libesp32_div/esp-nimble-cpp/src/NimBLE2904.h +++ b/lib/libesp32_div/esp-nimble-cpp/src/NimBLE2904.h @@ -33,9 +33,6 @@ struct BLE2904_Data { * @brief Descriptor for Characteristic Presentation Format. * * This is a convenience descriptor for the Characteristic Presentation Format which has a UUID of 0x2904. - * - * See also: - * https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.characteristic_presentation_format.xml */ class NimBLE2904: public NimBLEDescriptor { public: diff --git a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEAddress.cpp b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEAddress.cpp index d8ce5e88c..af7956b3c 100644 --- a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEAddress.cpp +++ b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEAddress.cpp @@ -138,6 +138,15 @@ uint8_t NimBLEAddress::getType() const { } // getType +/** + * @brief Determine if this address is a Resolvable Private Address. + * @return True if the address is a RPA. + */ +bool NimBLEAddress::isRpa() const { + return (m_addrType && ((m_address[5] & 0xc0) == 0x40)); +} // isRpa + + /** * @brief Convert a BLE address to a string. * diff --git a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEAddress.h b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEAddress.h index a6e10a09c..8a55b3ebf 100644 --- a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEAddress.h +++ b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEAddress.h @@ -43,6 +43,7 @@ public: NimBLEAddress(uint8_t address[6], uint8_t type = BLE_ADDR_PUBLIC); NimBLEAddress(const std::string &stringAddress, uint8_t type = BLE_ADDR_PUBLIC); NimBLEAddress(const uint64_t &address, uint8_t type = BLE_ADDR_PUBLIC); + bool isRpa() const; bool equals(const NimBLEAddress &otherAddress) const; const uint8_t* getNative() const; std::string toString() const; diff --git a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEAdvertisedDevice.cpp b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEAdvertisedDevice.cpp index 3ef71f541..bdc1358eb 100644 --- a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEAdvertisedDevice.cpp +++ b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEAdvertisedDevice.cpp @@ -203,6 +203,24 @@ std::string NimBLEAdvertisedDevice::getURI() { return ""; } // getURI +/** + * @brief Get the data from any type available in the advertisement + * @param [in] type The advertised data type BLE_HS_ADV_TYPE + * @return The data available under the type `type` +*/ +std::string NimBLEAdvertisedDevice::getPayloadByType(uint16_t type) { + size_t data_loc = 0; + + if(findAdvField(type, 0, &data_loc) > 0) { + ble_hs_adv_field *field = (ble_hs_adv_field *)&m_payload[data_loc]; + if(field->length > 1) { + return std::string((char*)field->value, field->length - 1); + } + } + + return ""; +} // getPayloadByType + /** * @brief Get the advertised name. @@ -556,6 +574,14 @@ bool NimBLEAdvertisedDevice::haveURI() { return findAdvField(BLE_HS_ADV_TYPE_URI) > 0; } // haveURI +/** + * @brief Does this advertisement have a adv type `type`? + * @return True if there is a `type` present. +*/ +bool NimBLEAdvertisedDevice::haveType(uint16_t type) { + return findAdvField(type) > 0; +} + /** * @brief Does the advertisement contain a target address? diff --git a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEAdvertisedDevice.h b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEAdvertisedDevice.h index 5b7a69f24..7869fb547 100644 --- a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEAdvertisedDevice.h +++ b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEAdvertisedDevice.h @@ -53,6 +53,7 @@ public: uint8_t getManufacturerDataCount(); std::string getManufacturerData(uint8_t index = 0); std::string getURI(); + std::string getPayloadByType(uint16_t type); /** * @brief A template to convert the service data to . @@ -134,6 +135,7 @@ public: bool haveAdvInterval(); bool haveTargetAddress(); bool haveURI(); + bool haveType(uint16_t type); std::string toString(); bool isConnectable(); bool isLegacyAdvertisement(); diff --git a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEAdvertising.cpp b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEAdvertising.cpp index 283f26493..0ce08ed58 100644 --- a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEAdvertising.cpp +++ b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEAdvertising.cpp @@ -96,8 +96,8 @@ void NimBLEAdvertising::addServiceUUID(const char* serviceUUID) { /** - * @brief Add a service uuid to exposed list of services. - * @param [in] serviceUUID The UUID of the service to expose. + * @brief Remove a service UUID from the advertisment. + * @param [in] serviceUUID The UUID of the service to remove. */ void NimBLEAdvertising::removeServiceUUID(const NimBLEUUID &serviceUUID) { for(auto it = m_serviceUUIDs.begin(); it != m_serviceUUIDs.end(); ++it) { @@ -110,10 +110,17 @@ void NimBLEAdvertising::removeServiceUUID(const NimBLEUUID &serviceUUID) { } // addServiceUUID +/** + * @brief Remove all service UUIDs from the advertisment. + */ +void NimBLEAdvertising::removeServices() { + std::vector().swap(m_serviceUUIDs); + m_advDataSet = false; +} // removeServices + + /** * @brief Set the device appearance in the advertising data. - * The codes for distinct appearances can be found here:\n - * https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.gap.appearance.xml. * @param [in] appearance The appearance of the device in the advertising data. */ void NimBLEAdvertising::setAppearance(uint16_t appearance) { @@ -137,7 +144,7 @@ void NimBLEAdvertising::addTxPower() { * @param [in] name The name to advertise. */ void NimBLEAdvertising::setName(const std::string &name) { - m_name.assign(name.begin(), name.end()); + std::vector(name.begin(), name.end()).swap(m_name); m_advData.name = &m_name[0]; m_advData.name_len = m_name.size(); m_advDataSet = false; @@ -149,7 +156,7 @@ void NimBLEAdvertising::setName(const std::string &name) { * @param [in] data The data to advertise. */ void NimBLEAdvertising::setManufacturerData(const std::string &data) { - m_mfgData.assign(data.begin(), data.end()); + std::vector(data.begin(), data.end()).swap(m_mfgData); m_advData.mfg_data = &m_mfgData[0]; m_advData.mfg_data_len = m_mfgData.size(); m_advDataSet = false; @@ -173,7 +180,7 @@ void NimBLEAdvertising::setManufacturerData(const std::vector &data) { * @param [in] uri The URI to advertise. */ void NimBLEAdvertising::setURI(const std::string &uri) { - m_uri.assign(uri.begin(), uri.end()); + std::vector(uri.begin(), uri.end()).swap(m_uri); m_advData.uri = &m_uri[0]; m_advData.uri_len = m_uri.size(); m_advDataSet = false; @@ -189,7 +196,8 @@ void NimBLEAdvertising::setURI(const std::string &uri) { void NimBLEAdvertising::setServiceData(const NimBLEUUID &uuid, const std::string &data) { switch (uuid.bitSize()) { case 16: { - m_svcData16.assign((uint8_t*)&uuid.getNative()->u16.value, (uint8_t*)&uuid.getNative()->u16.value + 2); + std::vector((uint8_t*)&uuid.getNative()->u16.value, + (uint8_t*)&uuid.getNative()->u16.value + 2).swap(m_svcData16); m_svcData16.insert(m_svcData16.end(), data.begin(), data.end()); m_advData.svc_data_uuid16 = (uint8_t*)&m_svcData16[0]; m_advData.svc_data_uuid16_len = (data.length() > 0) ? m_svcData16.size() : 0; @@ -197,7 +205,8 @@ void NimBLEAdvertising::setServiceData(const NimBLEUUID &uuid, const std::string } case 32: { - m_svcData32.assign((uint8_t*)&uuid.getNative()->u32.value, (uint8_t*)&uuid.getNative()->u32.value + 4); + std::vector((uint8_t*)&uuid.getNative()->u32.value, + (uint8_t*)&uuid.getNative()->u32.value + 4).swap(m_svcData32); m_svcData32.insert(m_svcData32.end(), data.begin(), data.end()); m_advData.svc_data_uuid32 = (uint8_t*)&m_svcData32[0]; m_advData.svc_data_uuid32_len = (data.length() > 0) ? m_svcData32.size() : 0; @@ -205,7 +214,8 @@ void NimBLEAdvertising::setServiceData(const NimBLEUUID &uuid, const std::string } case 128: { - m_svcData128.assign(uuid.getNative()->u128.value, uuid.getNative()->u128.value + 16); + std::vector(uuid.getNative()->u128.value, + uuid.getNative()->u128.value + 16).swap(m_svcData128); m_svcData128.insert(m_svcData128.end(), data.begin(), data.end()); m_advData.svc_data_uuid128 = (uint8_t*)&m_svcData128[0]; m_advData.svc_data_uuid128_len = (data.length() > 0) ? m_svcData128.size() : 0; @@ -402,7 +412,7 @@ void NimBLEAdvertising::setScanResponseData(NimBLEAdvertisementData& advertiseme * @param [in] dirAddr The address of a peer to directly advertise to. * @return True if advertising started successfully. */ -bool NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdvertising *pAdv), NimBLEAddress* dirAddr) { +bool NimBLEAdvertising::start(uint32_t duration, advCompleteCB_t advCompleteCB, NimBLEAddress* dirAddr) { NIMBLE_LOGD(LOG_TAG, ">> Advertising start: customAdvData: %d, customScanResponseData: %d", m_customAdvData, m_customScanResponseData); @@ -490,8 +500,8 @@ bool NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdv if(nullptr == (m_advData.uuids16 = (ble_uuid16_t*)realloc((void*)m_advData.uuids16, (m_advData.num_uuids16 + 1) * sizeof(ble_uuid16_t)))) { - NIMBLE_LOGC(LOG_TAG, "Error, no mem"); - abort(); + NIMBLE_LOGE(LOG_TAG, "Error, no mem"); + return false; } memcpy((void*)&m_advData.uuids16[m_advData.num_uuids16], &it.getNative()->u16, sizeof(ble_uuid16_t)); @@ -509,8 +519,8 @@ bool NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdv if(nullptr == (m_advData.uuids32 = (ble_uuid32_t*)realloc((void*)m_advData.uuids32, (m_advData.num_uuids32 + 1) * sizeof(ble_uuid32_t)))) { - NIMBLE_LOGC(LOG_TAG, "Error, no mem"); - abort(); + NIMBLE_LOGE(LOG_TAG, "Error, no mem"); + return false; } memcpy((void*)&m_advData.uuids32[m_advData.num_uuids32], &it.getNative()->u32, sizeof(ble_uuid32_t)); @@ -528,8 +538,8 @@ bool NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdv if(nullptr == (m_advData.uuids128 = (ble_uuid128_t*)realloc((void*)m_advData.uuids128, (m_advData.num_uuids128 + 1) * sizeof(ble_uuid128_t)))) { - NIMBLE_LOGC(LOG_TAG, "Error, no mem"); - abort(); + NIMBLE_LOGE(LOG_TAG, "Error, no mem"); + return false; } memcpy((void*)&m_advData.uuids128[m_advData.num_uuids128], &it.getNative()->u128, sizeof(ble_uuid128_t)); @@ -762,7 +772,7 @@ int NimBLEAdvertising::handleGapEvent(struct ble_gap_event *event, void *arg) { case BLE_HS_EOS: case BLE_HS_ECONTROLLER: case BLE_HS_ENOTSYNCED: - NIMBLE_LOGC(LOG_TAG, "host reset, rc=%d", event->adv_complete.reason); + NIMBLE_LOGE(LOG_TAG, "host reset, rc=%d", event->adv_complete.reason); NimBLEDevice::onReset(event->adv_complete.reason); return 0; default: @@ -800,9 +810,6 @@ void NimBLEAdvertisementData::addData(char * data, size_t length) { /** * @brief Set the appearance. * @param [in] appearance The appearance code value. - * - * See also: - * https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.gap.appearance.xml */ void NimBLEAdvertisementData::setAppearance(uint16_t appearance) { char cdata[2]; @@ -1069,4 +1076,12 @@ std::string NimBLEAdvertisementData::getPayload() { return m_payload; } // getPayload + +/** + * @brief Clear the advertisement data for reuse. + */ +void NimBLEAdvertisementData::clearData() { + m_payload.clear(); +} + #endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_BROADCASTER && !CONFIG_BT_NIMBLE_EXT_ADV */ diff --git a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEAdvertising.h b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEAdvertising.h index 14c34662c..dc36d0732 100644 --- a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEAdvertising.h +++ b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEAdvertising.h @@ -33,6 +33,7 @@ #include "NimBLEUUID.h" #include "NimBLEAddress.h" +#include #include /* COMPATIBILITY - DO NOT USE */ @@ -44,6 +45,9 @@ #define ESP_BLE_ADV_FLAG_NON_LIMIT_DISC (0x00 ) /* ************************* */ +class NimBLEAdvertising; + +typedef std::function advCompleteCB_t; /** * @brief Advertisement data set by the programmer to be published by the %BLE server. @@ -72,6 +76,7 @@ public: void addTxPower(); void setPreferredParams(uint16_t min, uint16_t max); std::string getPayload(); // Retrieve the current advert payload. + void clearData(); // Clear the advertisement data. private: friend class NimBLEAdvertising; @@ -92,7 +97,8 @@ public: void addServiceUUID(const NimBLEUUID &serviceUUID); void addServiceUUID(const char* serviceUUID); void removeServiceUUID(const NimBLEUUID &serviceUUID); - bool start(uint32_t duration = 0, void (*advCompleteCB)(NimBLEAdvertising *pAdv) = nullptr, NimBLEAddress* dirAddr = nullptr); + bool start(uint32_t duration = 0, advCompleteCB_t advCompleteCB = nullptr, NimBLEAddress* dirAddr = nullptr); + void removeServices(); bool stop(); void setAppearance(uint16_t appearance); void setName(const std::string &name); @@ -129,7 +135,7 @@ private: bool m_customScanResponseData; bool m_scanResp; bool m_advDataSet; - void (*m_advCompCB)(NimBLEAdvertising *pAdv); + advCompleteCB_t m_advCompCB{nullptr}; uint8_t m_slaveItvl[4]; uint32_t m_duration; std::vector m_svcData16; diff --git a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEAttValue.h b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEAttValue.h index be346d502..cc8599ab6 100644 --- a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEAttValue.h +++ b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEAttValue.h @@ -24,6 +24,7 @@ #include #include +#include #ifndef CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED # define CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED 0 @@ -41,7 +42,6 @@ # error CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH cannot be less than 1; Range = 1 : 512 #endif - /* Used to determine if the type passed to a template has a c_str() and length() method. */ template struct Has_c_str_len : std::false_type {}; @@ -266,7 +266,8 @@ public: /** @brief Subscript operator */ uint8_t operator [](int pos) const { - assert(pos < m_attr_len && "out of range"); return m_attr_value[pos]; } + NIMBLE_CPP_DEBUG_ASSERT(pos < m_attr_len); + return m_attr_value[pos]; } /** @brief Operator; Get the value as a std::vector. */ operator std::vector() const { @@ -311,7 +312,7 @@ public: inline NimBLEAttValue::NimBLEAttValue(uint16_t init_len, uint16_t max_len) { m_attr_value = (uint8_t*)calloc(init_len + 1, 1); - assert(m_attr_value && "No Mem"); + NIMBLE_CPP_DEBUG_ASSERT(m_attr_value); m_attr_max_len = std::min(BLE_ATT_ATTR_MAX_LEN, (int)max_len); m_attr_len = 0; m_capacity = init_len; @@ -354,7 +355,7 @@ inline NimBLEAttValue& NimBLEAttValue::operator =(const NimBLEAttValue & source) inline void NimBLEAttValue::deepCopy(const NimBLEAttValue & source) { uint8_t* res = (uint8_t*)realloc( m_attr_value, source.m_capacity + 1); - assert(res && "deepCopy: realloc failed"); + NIMBLE_CPP_DEBUG_ASSERT(res); ble_npl_hw_enter_critical(); m_attr_value = res; @@ -389,7 +390,7 @@ inline bool NimBLEAttValue::setValue(const uint8_t *value, uint16_t len) { res = (uint8_t*)realloc(m_attr_value, (len + 1)); m_capacity = len; } - assert(res && "setValue: realloc failed"); + NIMBLE_CPP_DEBUG_ASSERT(res); #if CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED time_t t = time(nullptr); @@ -424,7 +425,7 @@ inline NimBLEAttValue& NimBLEAttValue::append(const uint8_t *value, uint16_t len res = (uint8_t*)realloc(m_attr_value, (new_len + 1)); m_capacity = new_len; } - assert(res && "append: realloc failed"); + NIMBLE_CPP_DEBUG_ASSERT(res); #if CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED time_t t = time(nullptr); diff --git a/lib/libesp32_div/esp-nimble-cpp/src/NimBLECharacteristic.cpp b/lib/libesp32_div/esp-nimble-cpp/src/NimBLECharacteristic.cpp index f6db049de..82aa20fd1 100644 --- a/lib/libesp32_div/esp-nimble-cpp/src/NimBLECharacteristic.cpp +++ b/lib/libesp32_div/esp-nimble-cpp/src/NimBLECharacteristic.cpp @@ -87,9 +87,7 @@ NimBLEDescriptor* NimBLECharacteristic::createDescriptor(const char* uuid, uint3 */ NimBLEDescriptor* NimBLECharacteristic::createDescriptor(const NimBLEUUID &uuid, uint32_t properties, uint16_t max_len) { NimBLEDescriptor* pDescriptor = nullptr; - if(uuid == NimBLEUUID(uint16_t(0x2902))) { - assert(0 && "0x2902 descriptors cannot be manually created"); - } else if (uuid == NimBLEUUID(uint16_t(0x2904))) { + if (uuid == NimBLEUUID(uint16_t(0x2904))) { pDescriptor = new NimBLE2904(this); } else { pDescriptor = new NimBLEDescriptor(uuid, properties, max_len, this); @@ -266,7 +264,7 @@ int NimBLECharacteristic::handleGapEvent(uint16_t conn_handle, uint16_t attr_han NIMBLE_LOGW(LOG_TAG, "Conn_handle (%d) is above the maximum value (%d)", conn_handle, BLE_HCI_LE_CONN_HANDLE_MAX); return BLE_ATT_ERR_INVALID_HANDLE; } - + const ble_uuid_t *uuid; int rc; NimBLEConnInfo peerInfo; @@ -279,12 +277,12 @@ 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: { - rc = ble_gap_conn_find(conn_handle, &peerInfo.m_desc); - assert(rc == 0); + ble_gap_conn_find(conn_handle, &peerInfo.m_desc); // 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 || + conn_handle == BLE_HS_CONN_HANDLE_NONE || pCharacteristic->m_value.size() <= (ble_att_mtu(peerInfo.m_desc.conn_handle) - 3)) { pCharacteristic->m_pCallbacks->onRead(pCharacteristic, peerInfo); } @@ -316,8 +314,8 @@ int NimBLECharacteristic::handleGapEvent(uint16_t conn_handle, uint16_t attr_han len += next->om_len; next = SLIST_NEXT(next, om_next); } - rc = ble_gap_conn_find(conn_handle, &peerInfo.m_desc); - assert(rc == 0); + + ble_gap_conn_find(conn_handle, &peerInfo.m_desc); pCharacteristic->setValue(buf, len); pCharacteristic->m_pCallbacks->onWrite(pCharacteristic, peerInfo); return 0; diff --git a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEClient.cpp b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEClient.cpp index bd50e3221..e49827f42 100644 --- a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEClient.cpp +++ b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEClient.cpp @@ -111,7 +111,7 @@ NimBLEClient::~NimBLEClient() { */ void NimBLEClient::dcTimerCb(ble_npl_event *event) { /* NimBLEClient *pClient = (NimBLEClient*)event->arg; - NIMBLE_LOGC(LOG_TAG, "Timed out disconnecting from %s - resetting host", + NIMBLE_LOGE(LOG_TAG, "Timed out disconnecting from %s - resetting host", std::string(pClient->getPeerAddress()).c_str()); */ ble_hs_sched_reset(BLE_HS_ECONTROLLER); @@ -189,7 +189,7 @@ bool NimBLEClient::connect(const NimBLEAddress &address, bool deleteAttributes) NIMBLE_LOGD(LOG_TAG, ">> connect(%s)", address.toString().c_str()); if(!NimBLEDevice::m_synced) { - NIMBLE_LOGC(LOG_TAG, "Host reset, wait for sync."); + NIMBLE_LOGE(LOG_TAG, "Host reset, wait for sync."); return false; } @@ -458,9 +458,6 @@ void NimBLEClient::setConnectionParams(uint16_t minInterval, uint16_t maxInterva // 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"); } // setConnectionParams @@ -551,6 +548,66 @@ uint16_t NimBLEClient::getConnId() { return m_conn_id; } // getConnId +/** + * @brief Clear the connection information for this client. + * @note This is designed to be used to reset the connection information after + * calling setConnection(), and should not be used to disconnect from a + * peer. To disconnect from a peer, use disconnect(). + */ +void NimBLEClient::clearConnection() { + m_conn_id = BLE_HS_CONN_HANDLE_NONE; + m_connEstablished = false; + m_peerAddress = NimBLEAddress(); +} // clearConnection + +/** + * @brief Set the connection information for this client. + * @param [in] connInfo The connection information. + * @return True on success. + * @note Sets the connection established flag to true. + * @note If the client is already connected to a peer, this will return false. + * @note This is designed to be used when a connection is made outside of the + * NimBLEClient class, such as when a connection is made by the + * NimBLEServer class and the client is passed the connection id. This use + * enables the GATT Server to read the name of the device that has + * connected to it. + */ +bool NimBLEClient::setConnection(NimBLEConnInfo &connInfo) { + if (isConnected() || m_connEstablished) { + NIMBLE_LOGE(LOG_TAG, "Already connected"); + return false; + } + + m_peerAddress = connInfo.getAddress(); + m_conn_id = connInfo.getConnHandle(); + m_connEstablished = true; + + return true; +} // setConnection + +/** + * @brief Set the connection information for this client. + * @param [in] conn_id The connection id. + * @note Sets the connection established flag to true. + * @note This is designed to be used when a connection is made outside of the + * NimBLEClient class, such as when a connection is made by the + * NimBLEServer class and the client is passed the connection id. This use + * enables the GATT Server to read the name of the device that has + * connected to it. + * @note If the client is already connected to a peer, this will return false. + * @note This will look up the peer address using the connection id. + */ +bool NimBLEClient::setConnection(uint16_t conn_id) { + // we weren't provided the peer address, look it up using ble_gap_conn_find + NimBLEConnInfo connInfo; + int rc = ble_gap_conn_find(m_conn_id, &connInfo.m_desc); + if (rc != 0) { + NIMBLE_LOGE(LOG_TAG, "Connection info not found"); + return false; + } + + return setConnection(connInfo); +} // setConnection /** * @brief Retrieve the address of the peer. @@ -945,7 +1002,7 @@ int NimBLEClient::handleGapEvent(struct ble_gap_event *event, void *arg) { case BLE_HS_ETIMEOUT_HCI: case BLE_HS_ENOTSYNCED: case BLE_HS_EOS: - NIMBLE_LOGC(LOG_TAG, "Disconnect - host reset, rc=%d", rc); + NIMBLE_LOGE(LOG_TAG, "Disconnect - host reset, rc=%d", rc); NimBLEDevice::onReset(rc); break; default: @@ -1111,7 +1168,11 @@ int NimBLEClient::handleGapEvent(struct ble_gap_event *event, void *arg) { { NimBLEConnInfo peerInfo; rc = ble_gap_conn_find(event->enc_change.conn_handle, &peerInfo.m_desc); - assert(rc == 0); + if (rc != 0) { + NIMBLE_LOGE(LOG_TAG, "Connection info not found"); + rc = 0; + break; + } if (event->enc_change.status == (BLE_HS_ERR_HCI_BASE + BLE_ERR_PINKEY_MISSING)) { // Key is missing, try deleting. @@ -1125,6 +1186,19 @@ int NimBLEClient::handleGapEvent(struct ble_gap_event *event, void *arg) { break; } //BLE_GAP_EVENT_ENC_CHANGE + case BLE_GAP_EVENT_IDENTITY_RESOLVED: { + NimBLEConnInfo peerInfo; + rc = ble_gap_conn_find(event->identity_resolved.conn_handle, &peerInfo.m_desc); + if (rc != 0) { + NIMBLE_LOGE(LOG_TAG, "Connection info not found"); + rc = 0; + break; + } + + pClient->m_pClientCallbacks->onIdentity(peerInfo); + break; + } // BLE_GAP_EVENT_IDENTITY_RESOLVED + case BLE_GAP_EVENT_MTU: { if(pClient->m_conn_id != event->mtu.conn_handle){ return 0; @@ -1143,20 +1217,17 @@ int NimBLEClient::handleGapEvent(struct ble_gap_event *event, void *arg) { if(pClient->m_conn_id != event->passkey.conn_handle) return 0; - if (event->passkey.params.action == BLE_SM_IOACT_DISP) { - pkey.action = event->passkey.params.action; - pkey.passkey = NimBLEDevice::m_passkey; // This is the passkey to be entered on peer - rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey); - NIMBLE_LOGD(LOG_TAG, "ble_sm_inject_io result: %d", rc); + NimBLEConnInfo peerInfo; + rc = ble_gap_conn_find(event->passkey.conn_handle, &peerInfo.m_desc); + if (rc != 0) { + NIMBLE_LOGE(LOG_TAG, "Connection info not found"); + rc = 0; + break; + } - } else if (event->passkey.params.action == BLE_SM_IOACT_NUMCMP) { + if (event->passkey.params.action == BLE_SM_IOACT_NUMCMP) { NIMBLE_LOGD(LOG_TAG, "Passkey on device's display: %" PRIu32, event->passkey.params.numcmp); - pkey.action = event->passkey.params.action; - pkey.numcmp_accept = pClient->m_pClientCallbacks->onConfirmPIN(event->passkey.params.numcmp); - - rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey); - NIMBLE_LOGD(LOG_TAG, "ble_sm_inject_io result: %d", rc); - + pClient->m_pClientCallbacks->onConfirmPIN(peerInfo, event->passkey.params.numcmp); //TODO: Handle out of band pairing } else if (event->passkey.params.action == BLE_SM_IOACT_OOB) { static uint8_t tem_oob[16] = {0}; @@ -1169,12 +1240,7 @@ int NimBLEClient::handleGapEvent(struct ble_gap_event *event, void *arg) { //////// } else if (event->passkey.params.action == BLE_SM_IOACT_INPUT) { NIMBLE_LOGD(LOG_TAG, "Enter the passkey"); - pkey.action = event->passkey.params.action; - pkey.passkey = pClient->m_pClientCallbacks->onPassKeyRequest(); - - rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey); - NIMBLE_LOGD(LOG_TAG, "ble_sm_inject_io result: %d", rc); - + pClient->m_pClientCallbacks->onPassKeyEntry(peerInfo); } else if (event->passkey.params.action == BLE_SM_IOACT_NONE) { NIMBLE_LOGD(LOG_TAG, "No passkey action required"); } @@ -1261,17 +1327,22 @@ bool NimBLEClientCallbacks::onConnParamsUpdateRequest(NimBLEClient* pClient, con return true; } -uint32_t NimBLEClientCallbacks::onPassKeyRequest(){ - NIMBLE_LOGD("NimBLEClientCallbacks", "onPassKeyRequest: default: 123456"); - return 123456; -} +void NimBLEClientCallbacks::onPassKeyEntry(const NimBLEConnInfo& connInfo){ + NIMBLE_LOGD("NimBLEClientCallbacks", "onPassKeyEntry: default: 123456"); + NimBLEDevice::injectPassKey(connInfo, 123456); +} //onPassKeyEntry -void NimBLEClientCallbacks::onAuthenticationComplete(NimBLEConnInfo& peerInfo){ +void NimBLEClientCallbacks::onAuthenticationComplete(const NimBLEConnInfo& connInfo){ NIMBLE_LOGD("NimBLEClientCallbacks", "onAuthenticationComplete: default"); } -bool NimBLEClientCallbacks::onConfirmPIN(uint32_t pin){ + +void NimBLEClientCallbacks::onIdentity(const NimBLEConnInfo& connInfo){ + NIMBLE_LOGD("NimBLEClientCallbacks", "onIdentity: default"); +} // onIdentity + +void NimBLEClientCallbacks::onConfirmPIN(const NimBLEConnInfo& connInfo, uint32_t pin){ NIMBLE_LOGD("NimBLEClientCallbacks", "onConfirmPIN: default: true"); - return true; + NimBLEDevice::injectConfirmPIN(connInfo, true); } #endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL */ diff --git a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEClient.h b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEClient.h index 486b3d0b5..acef487bf 100644 --- a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEClient.h +++ b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEClient.h @@ -61,6 +61,9 @@ public: bool deleteCallbacks = true); std::string toString(); uint16_t getConnId(); + void clearConnection(); + bool setConnection(NimBLEConnInfo &conn_info); + bool setConnection(uint16_t conn_id); uint16_t getMTU(); bool secureConnection(); void setConnectTimeout(uint32_t timeout); @@ -144,23 +147,29 @@ public: /** * @brief Called when server requests a passkey for pairing. - * @return The passkey to be sent to the server. + * @param [in] connInfo A reference to a NimBLEConnInfo instance containing the peer info. */ - virtual uint32_t onPassKeyRequest(); + virtual void onPassKeyEntry(const NimBLEConnInfo& connInfo); /** * @brief Called when the pairing procedure is complete. * @param [in] connInfo A reference to a NimBLEConnInfo instance containing the peer info.\n * This can be used to check the status of the connection encryption/pairing. */ - virtual void onAuthenticationComplete(NimBLEConnInfo& connInfo); + virtual void onAuthenticationComplete(const NimBLEConnInfo& connInfo); /** * @brief Called when using numeric comparision for pairing. + * @param [in] connInfo A reference to a NimBLEConnInfo instance containing the peer info. * @param [in] pin The pin to compare with the server. - * @return True to accept the pin. */ - virtual bool onConfirmPIN(uint32_t pin); + virtual void onConfirmPIN(const NimBLEConnInfo& connInfo, uint32_t pin); + + /** + * @brief Called when the peer identity address is resolved. + * @param [in] connInfo A reference to a NimBLEConnInfo instance with information + */ + virtual void onIdentity(const NimBLEConnInfo& connInfo); }; #endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL */ diff --git a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEConnInfo.h b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEConnInfo.h index c764c2462..274e6d3b0 100644 --- a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEConnInfo.h +++ b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEConnInfo.h @@ -12,47 +12,47 @@ friend class NimBLEClient; friend class NimBLECharacteristic; friend class NimBLEDescriptor; - ble_gap_conn_desc m_desc; - NimBLEConnInfo() { m_desc = {}; } - NimBLEConnInfo(ble_gap_conn_desc desc) { m_desc = desc; } + ble_gap_conn_desc m_desc{}; + NimBLEConnInfo(){}; + NimBLEConnInfo(ble_gap_conn_desc desc) { m_desc = desc; } public: /** @brief Gets the over-the-air address of the connected peer */ - NimBLEAddress getAddress() { return NimBLEAddress(m_desc.peer_ota_addr); } + NimBLEAddress getAddress() const { return NimBLEAddress(m_desc.peer_ota_addr); } /** @brief Gets the ID address of the connected peer */ - NimBLEAddress getIdAddress() { return NimBLEAddress(m_desc.peer_id_addr); } + NimBLEAddress getIdAddress() const { return NimBLEAddress(m_desc.peer_id_addr); } - /** @brief Gets the connection handle of the connected peer */ - uint16_t getConnHandle() { return m_desc.conn_handle; } + /** @brief Gets the connection handle (also known as the connection id) of the connected peer */ + uint16_t getConnHandle() const { return m_desc.conn_handle; } /** @brief Gets the connection interval for this connection (in 1.25ms units) */ - uint16_t getConnInterval() { return m_desc.conn_itvl; } + uint16_t getConnInterval() const { return m_desc.conn_itvl; } /** @brief Gets the supervision timeout for this connection (in 10ms units) */ - uint16_t getConnTimeout() { return m_desc.supervision_timeout; } + uint16_t getConnTimeout() const { return m_desc.supervision_timeout; } /** @brief Gets the allowable latency for this connection (unit = number of intervals) */ - uint16_t getConnLatency() { return m_desc.conn_latency; } + uint16_t getConnLatency() const { return m_desc.conn_latency; } /** @brief Gets the maximum transmission unit size for this connection (in bytes) */ - uint16_t getMTU() { return ble_att_mtu(m_desc.conn_handle); } + uint16_t getMTU() const { return ble_att_mtu(m_desc.conn_handle); } /** @brief Check if we are in the master role in this connection */ - bool isMaster() { return (m_desc.role == BLE_GAP_ROLE_MASTER); } + bool isMaster() const { return (m_desc.role == BLE_GAP_ROLE_MASTER); } /** @brief Check if we are in the slave role in this connection */ - bool isSlave() { return (m_desc.role == BLE_GAP_ROLE_SLAVE); } + bool isSlave() const { return (m_desc.role == BLE_GAP_ROLE_SLAVE); } /** @brief Check if we are connected to a bonded peer */ - bool isBonded() { return (m_desc.sec_state.bonded == 1); } + bool isBonded() const { return (m_desc.sec_state.bonded == 1); } /** @brief Check if the connection in encrypted */ - bool isEncrypted() { return (m_desc.sec_state.encrypted == 1); } + bool isEncrypted() const { return (m_desc.sec_state.encrypted == 1); } /** @brief Check if the the connection has been authenticated */ - bool isAuthenticated() { return (m_desc.sec_state.authenticated == 1); } + bool isAuthenticated() const { return (m_desc.sec_state.authenticated == 1); } /** @brief Gets the key size used to encrypt the connection */ - uint8_t getSecKeySize() { return m_desc.sec_state.key_size; } + uint8_t getSecKeySize() const { return m_desc.sec_state.key_size; } }; #endif diff --git a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEDescriptor.cpp b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEDescriptor.cpp index 28a151315..a457782a8 100644 --- a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEDescriptor.cpp +++ b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEDescriptor.cpp @@ -55,7 +55,14 @@ NimBLEDescriptor::NimBLEDescriptor(NimBLEUUID uuid, uint16_t properties, uint16_ m_pCharacteristic = pCharacteristic; m_pCallbacks = &defaultCallbacks; // No initial callback. m_properties = 0; - m_removed = 0; + + // Check if this is the client configuration descriptor and set to removed if true. + if (uuid == NimBLEUUID((uint16_t)0x2902)) { + NIMBLE_LOGW(LOG_TAG, "Manually created 2902 descriptor has no functionality; please remove."); + m_removed = 1; + } else { + m_removed = 0; + } if (properties & BLE_GATT_CHR_F_READ) { // convert uint16_t properties to uint8_t m_properties |= BLE_ATT_F_READ; @@ -155,7 +162,7 @@ int NimBLEDescriptor::handleGapEvent(uint16_t conn_handle, uint16_t attr_handle, const ble_uuid_t *uuid; int rc; - NimBLEConnInfo peerInfo; + NimBLEConnInfo peerInfo{}; NimBLEDescriptor* pDescriptor = (NimBLEDescriptor*)arg; NIMBLE_LOGD(LOG_TAG, "Descriptor %s %s event", pDescriptor->getUUID().toString().c_str(), @@ -165,12 +172,12 @@ int NimBLEDescriptor::handleGapEvent(uint16_t conn_handle, uint16_t attr_handle, if(ble_uuid_cmp(uuid, &pDescriptor->getUUID().getNative()->u) == 0){ switch(ctxt->op) { case BLE_GATT_ACCESS_OP_READ_DSC: { - rc = ble_gap_conn_find(conn_handle, &peerInfo.m_desc); - assert(rc == 0); + ble_gap_conn_find(conn_handle, &peerInfo.m_desc); // 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 || + conn_handle == BLE_HS_CONN_HANDLE_NONE || pDescriptor->m_value.size() <= (ble_att_mtu(peerInfo.getConnHandle()) - 3)) { pDescriptor->m_pCallbacks->onRead(pDescriptor, peerInfo); } @@ -182,11 +189,9 @@ int NimBLEDescriptor::handleGapEvent(uint16_t conn_handle, uint16_t attr_handle, } case BLE_GATT_ACCESS_OP_WRITE_DSC: { - rc = ble_gap_conn_find(conn_handle, &peerInfo.m_desc); - assert(rc == 0); + ble_gap_conn_find(conn_handle, &peerInfo.m_desc); uint16_t att_max_len = pDescriptor->m_value.max_size(); - if (ctxt->om->om_len > att_max_len) { return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; } diff --git a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEDevice.cpp b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEDevice.cpp index eb9c17703..d14ce62d7 100644 --- a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEDevice.cpp +++ b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEDevice.cpp @@ -299,7 +299,7 @@ size_t NimBLEDevice::getClientListSize() { /** * @brief Get a reference to a client by connection ID. * @param [in] conn_id The client connection ID to search for. - * @return A pointer to the client object with the spcified connection ID. + * @return A pointer to the client object with the specified connection ID or nullptr. */ /* STATIC */ NimBLEClient* NimBLEDevice::getClientByID(uint16_t conn_id) { @@ -308,7 +308,7 @@ NimBLEClient* NimBLEDevice::getClientByID(uint16_t conn_id) { return (*it); } } - assert(0); + return nullptr; } // getClientByID @@ -567,10 +567,16 @@ int NimBLEDevice::getNumBonds() { /** * @brief Deletes all bonding information. + * @returns true on success, false on failure. */ /*STATIC*/ -void NimBLEDevice::deleteAllBonds() { - ble_store_clear(); +bool NimBLEDevice::deleteAllBonds() { + int rc = ble_store_clear(); + if (rc != 0) { + NIMBLE_LOGE(LOG_TAG, "Failed to delete all bonds; rc=%d", rc); + return false; + } + return true; } @@ -685,6 +691,7 @@ bool NimBLEDevice::whiteListAdd(const NimBLEAddress & address) { int rc = ble_gap_wl_set(&wlVec[0], wlVec.size()); if (rc != 0) { NIMBLE_LOGE(LOG_TAG, "Failed adding to whitelist rc=%d", rc); + m_whiteList.pop_back(); return false; } @@ -771,7 +778,7 @@ void NimBLEDevice::onReset(int reason) m_synced = false; - NIMBLE_LOGC(LOG_TAG, "Resetting state; reason=%d, %s", reason, + NIMBLE_LOGE(LOG_TAG, "Resetting state; reason=%d, %s", reason, NimBLEUtils::returnCodeToString(reason)); #if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER) @@ -799,7 +806,10 @@ void NimBLEDevice::onSync(void) /* Make sure we have proper identity address set (public preferred) */ int rc = ble_hs_util_ensure_addr(0); - assert(rc == 0); + if (rc != 0) { + NIMBLE_LOGE(LOG_TAG, "error ensuring address; rc=%d", rc); + return; + } #ifndef ESP_PLATFORM rc = ble_hs_id_infer_auto(m_own_addr_type, &m_own_addr_type); @@ -871,9 +881,11 @@ void NimBLEDevice::init(const std::string &deviceName) { ESP_ERROR_CHECK(errRc); +#if CONFIG_IDF_TARGET_ESP32 esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT); +#endif -#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0) +#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0) | !defined(CONFIG_NIMBLE_CPP_IDF) esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT(); # if defined (CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S3) bt_cfg.bluetooth_mode = ESP_BT_MODE_BLE; @@ -902,14 +914,16 @@ void NimBLEDevice::init(const std::string &deviceName) { ble_hs_cfg.sm_bonding = 0; ble_hs_cfg.sm_mitm = 0; ble_hs_cfg.sm_sc = 1; - ble_hs_cfg.sm_our_key_dist = 1; - ble_hs_cfg.sm_their_key_dist = 3; + ble_hs_cfg.sm_our_key_dist = BLE_SM_PAIR_KEY_DIST_ENC | BLE_SM_PAIR_KEY_DIST_ID; + ble_hs_cfg.sm_their_key_dist = BLE_SM_PAIR_KEY_DIST_ENC | BLE_SM_PAIR_KEY_DIST_ID; ble_hs_cfg.store_status_cb = ble_store_util_status_rr; /*TODO: Implement handler for this*/ // Set the device name. rc = ble_svc_gap_device_name_set(deviceName.c_str()); - assert(rc == 0); + if (rc != 0) { + NIMBLE_LOGE(LOG_TAG, "ble_svc_gap_device_name_set() failed; rc=%d", rc); + } ble_store_config_init(); @@ -935,13 +949,13 @@ void NimBLEDevice::deinit(bool clearAll) { int ret = nimble_port_stop(); if (ret == 0) { nimble_port_deinit(); -#ifdef ESP_PLATFORM -#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0) +#ifdef CONFIG_NIMBLE_CPP_IDF +# if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0) ret = esp_nimble_hci_and_controller_deinit(); if (ret != ESP_OK) { NIMBLE_LOGE(LOG_TAG, "esp_nimble_hci_and_controller_deinit() failed with error: %d", ret); } -#endif +# endif #endif initialized = false; m_synced = false; @@ -1150,6 +1164,43 @@ int NimBLEDevice::startSecurity(uint16_t conn_id) { } // startSecurity +/** + * @brief Inject the provided passkey into the Security Manager + * @param [in] peerInfo Connection information for the peer + * @param [in] pin The 6-digit pin to inject + * @return true if the passkey was injected successfully. + */ +bool NimBLEDevice::injectPassKey(const NimBLEConnInfo& peerInfo, uint32_t pin) { + int rc = 0; + struct ble_sm_io pkey = {0,0}; + + pkey.action = BLE_SM_IOACT_INPUT; + pkey.passkey = pin; + + rc = ble_sm_inject_io(peerInfo.getConnHandle(), &pkey); + NIMBLE_LOGD(LOG_TAG, "BLE_SM_IOACT_INPUT; ble_sm_inject_io result: %d", rc); + return rc == 0; +} + + +/** + * @brief Inject the provided numeric comparison response into the Security Manager + * @param [in] peerInfo Connection information for the peer + * @param [in] accept Whether the user confirmed or declined the comparison + */ +bool NimBLEDevice::injectConfirmPIN(const NimBLEConnInfo& peerInfo, bool accept) { + int rc = 0; + struct ble_sm_io pkey = {0,0}; + + pkey.action = BLE_SM_IOACT_NUMCMP; + pkey.numcmp_accept = accept; + + rc = ble_sm_inject_io(peerInfo.getConnHandle(), &pkey); + NIMBLE_LOGD(LOG_TAG, "BLE_SM_IOACT_NUMCMP; ble_sm_inject_io result: %d", rc); + return rc == 0; +} + + /** * @brief Check if the device address is on our ignore list. * @param [in] address The address to look for. @@ -1202,10 +1253,22 @@ void NimBLEDevice::setCustomGapHandler(gap_event_handler handler) { int rc = ble_gap_event_listener_register(&m_listener, m_customGapHandler, NULL); if(rc == BLE_HS_EALREADY){ NIMBLE_LOGI(LOG_TAG, "Already listening to GAP events."); + } else if (rc != 0) { + NIMBLE_LOGE(LOG_TAG, "ble_gap_event_listener_register: rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc)); } - else{ - assert(rc == 0); - } + } // setCustomGapHandler -#endif // CONFIG_BT_ENABLED +#if CONFIG_NIMBLE_CPP_DEBUG_ASSERT_ENABLED || __DOXYGEN__ +/** + * @brief Debug assert - weak function. + * @param [in] file The file where the assert occurred. + * @param [in] line The line number where the assert occurred. + */ +void nimble_cpp_assert(const char *file, unsigned line) { + NIMBLE_LOGC("", "Assertion failed at %s:%u\n", file, line); + abort(); +} +#endif // CONFIG_NIMBLE_CPP_DEBUG_ASSERT_ENABLED + +#endif // CONFIG_BT_ENABLED \ No newline at end of file diff --git a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEDevice.h b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEDevice.h index ddb7a60c5..64bd4ed34 100644 --- a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEDevice.h +++ b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEDevice.h @@ -136,6 +136,8 @@ public: static void setSecurityPasskey(uint32_t pin); static uint32_t getSecurityPasskey(); static int startSecurity(uint16_t conn_id); + static bool injectConfirmPIN(const NimBLEConnInfo& peerInfo, bool accept); + static bool injectPassKey(const NimBLEConnInfo& peerInfo, uint32_t pin); static int setMTU(uint16_t mtu); static uint16_t getMTU(); static bool isIgnored(const NimBLEAddress &address); @@ -172,7 +174,7 @@ public: static bool deleteBond(const NimBLEAddress &address); static int getNumBonds(); static bool isBonded(const NimBLEAddress &address); - static void deleteAllBonds(); + static bool deleteAllBonds(); static NimBLEAddress getBondedAddress(int index); #endif diff --git a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEEddystoneTLM.cpp b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEEddystoneTLM.cpp index 7a9c5102c..1f48a1609 100644 --- a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEEddystoneTLM.cpp +++ b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEEddystoneTLM.cpp @@ -81,7 +81,7 @@ uint16_t NimBLEEddystoneTLM::getVolt() { * @return The temperature value. */ float NimBLEEddystoneTLM::getTemp() { - return ENDIAN_CHANGE_U16(m_eddystoneData.temp) / 256.0f; + return (int16_t)ENDIAN_CHANGE_U16(m_eddystoneData.temp) / 256.0f; } // getTemp /** @@ -203,7 +203,7 @@ void NimBLEEddystoneTLM::setVolt(uint16_t volt) { * @param [in] temp The temperature value. */ void NimBLEEddystoneTLM::setTemp(float temp) { - m_eddystoneData.temp = (uint16_t)temp; + m_eddystoneData.temp = ENDIAN_CHANGE_U16((int16_t)(temp * 256.0f)); } // setTemp diff --git a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEExtAdvertising.cpp b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEExtAdvertising.cpp index b979c9fd0..67d8a586a 100644 --- a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEExtAdvertising.cpp +++ b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEExtAdvertising.cpp @@ -341,7 +341,7 @@ int NimBLEExtAdvertising::handleGapEvent(struct ble_gap_event *event, void *arg) case BLE_HS_EOS: case BLE_HS_ECONTROLLER: case BLE_HS_ENOTSYNCED: - NIMBLE_LOGC(LOG_TAG, "host reset, rc = %d", event->adv_complete.reason); + NIMBLE_LOGE(LOG_TAG, "host reset, rc = %d", event->adv_complete.reason); NimBLEDevice::onReset(event->adv_complete.reason); return 0; default: @@ -623,9 +623,6 @@ void NimBLEExtAdvertisement::addData(const uint8_t * data, size_t length) { /** * @brief Set the appearance. * @param [in] appearance The appearance code value. - * - * See also: - * https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.gap.appearance.xml */ void NimBLEExtAdvertisement::setAppearance(uint16_t appearance) { char cdata[2]; diff --git a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEHIDDevice.cpp b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEHIDDevice.cpp index a2310eb9e..12c14def1 100644 --- a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEHIDDevice.cpp +++ b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEHIDDevice.cpp @@ -26,28 +26,34 @@ NimBLEHIDDevice::NimBLEHIDDevice(NimBLEServer* server) { /* * Here we create mandatory services described in bluetooth specification */ - m_deviceInfoService = server->createService(NimBLEUUID((uint16_t) 0x180a)); - m_hidService = server->createService(NimBLEUUID((uint16_t) 0x1812)); - m_batteryService = server->createService(NimBLEUUID((uint16_t) 0x180f)); + m_deviceInfoService = server->createService(NimBLEUUID((uint16_t)0x180a)); + m_hidService = server->createService(NimBLEUUID((uint16_t)0x1812)); + m_batteryService = server->createService(NimBLEUUID((uint16_t)0x180f)); /* * Mandatory characteristic for device info service */ - m_pnpCharacteristic = m_deviceInfoService->createCharacteristic((uint16_t) 0x2a50, NIMBLE_PROPERTY::READ); + m_pnpCharacteristic = m_deviceInfoService->createCharacteristic((uint16_t)0x2a50, NIMBLE_PROPERTY::READ); + + /* + * Non-mandatory characteristics for device info service + * Will be created on demand + */ + m_manufacturerCharacteristic = nullptr; /* * Mandatory characteristics for HID service */ - m_hidInfoCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a4a, NIMBLE_PROPERTY::READ); - m_reportMapCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a4b, NIMBLE_PROPERTY::READ); - m_hidControlCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a4c, NIMBLE_PROPERTY::WRITE_NR); - m_protocolModeCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a4e, NIMBLE_PROPERTY::WRITE_NR | NIMBLE_PROPERTY::READ); + m_hidInfoCharacteristic = m_hidService->createCharacteristic((uint16_t)0x2a4a, NIMBLE_PROPERTY::READ); + m_reportMapCharacteristic = m_hidService->createCharacteristic((uint16_t)0x2a4b, NIMBLE_PROPERTY::READ); + m_hidControlCharacteristic = m_hidService->createCharacteristic((uint16_t)0x2a4c, NIMBLE_PROPERTY::WRITE_NR); + m_protocolModeCharacteristic = m_hidService->createCharacteristic((uint16_t)0x2a4e, NIMBLE_PROPERTY::WRITE_NR | NIMBLE_PROPERTY::READ); /* * Mandatory battery level characteristic with notification and presence descriptor */ - m_batteryLevelCharacteristic = m_batteryService->createCharacteristic((uint16_t) 0x2a19, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::NOTIFY); - NimBLE2904* batteryLevelDescriptor = (NimBLE2904*)m_batteryLevelCharacteristic->createDescriptor((uint16_t) 0x2904); + m_batteryLevelCharacteristic = m_batteryService->createCharacteristic((uint16_t)0x2a19, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::NOTIFY); + NimBLE2904 *batteryLevelDescriptor = (NimBLE2904*)m_batteryLevelCharacteristic->createDescriptor((uint16_t)0x2904); batteryLevelDescriptor->setFormat(NimBLE2904::FORMAT_UINT8); batteryLevelDescriptor->setNamespace(1); batteryLevelDescriptor->setUnit(0x27ad); @@ -56,8 +62,8 @@ NimBLEHIDDevice::NimBLEHIDDevice(NimBLEServer* server) { * This value is setup here because its default value in most usage cases, its very rare to use boot mode * and we want to simplify library using as much as possible */ - const uint8_t pMode[] = { 0x01 }; - protocolMode()->setValue((uint8_t*) pMode, 1); + const uint8_t pMode[] = {0x01}; + protocolMode()->setValue((uint8_t*)pMode, 1); } NimBLEHIDDevice::~NimBLEHIDDevice() { @@ -86,7 +92,10 @@ void NimBLEHIDDevice::startServices() { * @brief Create a manufacturer characteristic (this characteristic is optional). */ NimBLECharacteristic* NimBLEHIDDevice::manufacturer() { - m_manufacturerCharacteristic = m_deviceInfoService->createCharacteristic((uint16_t) 0x2a29, NIMBLE_PROPERTY::READ); + if (m_manufacturerCharacteristic == nullptr) { + m_manufacturerCharacteristic = m_deviceInfoService->createCharacteristic((uint16_t)0x2a29, NIMBLE_PROPERTY::READ); + } + return m_manufacturerCharacteristic; } @@ -95,7 +104,7 @@ NimBLECharacteristic* NimBLEHIDDevice::manufacturer() { * @param [in] name The manufacturer name of this HID device. */ void NimBLEHIDDevice::manufacturer(std::string name) { - m_manufacturerCharacteristic->setValue(name); + manufacturer()->setValue(name); } /** @@ -106,7 +115,15 @@ void NimBLEHIDDevice::manufacturer(std::string name) { * @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 }; + uint8_t pnp[] = { + sig, + ((uint8_t*)&vid)[0], + ((uint8_t*)&vid)[1], + ((uint8_t*)&pid)[0], + ((uint8_t*)&pid)[1], + ((uint8_t*)&version)[0], + ((uint8_t*)&version)[1] + }; m_pnpCharacteristic->setValue(pnp, sizeof(pnp)); } @@ -116,7 +133,7 @@ void NimBLEHIDDevice::pnp(uint8_t sig, uint16_t vid, uint16_t pid, uint16_t vers * @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 }; + uint8_t info[] = {0x11, 0x1, country, flags}; m_hidInfoCharacteristic->setValue(info, sizeof(info)); } @@ -126,11 +143,11 @@ void NimBLEHIDDevice::hidInfo(uint8_t country, uint8_t flags) { * @return pointer to new input report characteristic */ NimBLECharacteristic* NimBLEHIDDevice::inputReport(uint8_t reportID) { - NimBLECharacteristic* inputReportCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a4d, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::NOTIFY | NIMBLE_PROPERTY::READ_ENC); - NimBLEDescriptor* inputReportDescriptor = inputReportCharacteristic->createDescriptor((uint16_t) 0x2908, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::READ_ENC); + NimBLECharacteristic *inputReportCharacteristic = m_hidService->createCharacteristic((uint16_t)0x2a4d, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::NOTIFY | NIMBLE_PROPERTY::READ_ENC); + NimBLEDescriptor *inputReportDescriptor = inputReportCharacteristic->createDescriptor((uint16_t)0x2908, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::READ_ENC); - uint8_t desc1_val[] = { reportID, 0x01 }; - inputReportDescriptor->setValue((uint8_t*) desc1_val, 2); + uint8_t desc1_val[] = {reportID, 0x01}; + inputReportDescriptor->setValue((uint8_t*)desc1_val, 2); return inputReportCharacteristic; } @@ -141,11 +158,11 @@ NimBLECharacteristic* NimBLEHIDDevice::inputReport(uint8_t reportID) { * @return Pointer to new output report characteristic */ NimBLECharacteristic* NimBLEHIDDevice::outputReport(uint8_t reportID) { - NimBLECharacteristic* outputReportCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a4d, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::WRITE_NR | NIMBLE_PROPERTY::READ_ENC | NIMBLE_PROPERTY::WRITE_ENC); - NimBLEDescriptor* outputReportDescriptor = outputReportCharacteristic->createDescriptor((uint16_t) 0x2908, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::READ_ENC | NIMBLE_PROPERTY::WRITE_ENC); + NimBLECharacteristic *outputReportCharacteristic = m_hidService->createCharacteristic((uint16_t)0x2a4d, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::WRITE_NR | NIMBLE_PROPERTY::READ_ENC | NIMBLE_PROPERTY::WRITE_ENC); + NimBLEDescriptor *outputReportDescriptor = outputReportCharacteristic->createDescriptor((uint16_t)0x2908, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::READ_ENC | NIMBLE_PROPERTY::WRITE_ENC); - uint8_t desc1_val[] = { reportID, 0x02 }; - outputReportDescriptor->setValue((uint8_t*) desc1_val, 2); + uint8_t desc1_val[] = {reportID, 0x02}; + outputReportDescriptor->setValue((uint8_t*)desc1_val, 2); return outputReportCharacteristic; } @@ -156,11 +173,11 @@ NimBLECharacteristic* NimBLEHIDDevice::outputReport(uint8_t reportID) { * @return Pointer to new feature report characteristic */ NimBLECharacteristic* NimBLEHIDDevice::featureReport(uint8_t reportID) { - NimBLECharacteristic* featureReportCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a4d, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::READ_ENC | NIMBLE_PROPERTY::WRITE_ENC); - NimBLEDescriptor* featureReportDescriptor = featureReportCharacteristic->createDescriptor((uint16_t) 0x2908, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::READ_ENC | NIMBLE_PROPERTY::WRITE_ENC); + NimBLECharacteristic *featureReportCharacteristic = m_hidService->createCharacteristic((uint16_t)0x2a4d, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::READ_ENC | NIMBLE_PROPERTY::WRITE_ENC); + NimBLEDescriptor *featureReportDescriptor = featureReportCharacteristic->createDescriptor((uint16_t)0x2908, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::READ_ENC | NIMBLE_PROPERTY::WRITE_ENC); - uint8_t desc1_val[] = { reportID, 0x03 }; - featureReportDescriptor->setValue((uint8_t*) desc1_val, 2); + uint8_t desc1_val[] = {reportID, 0x03}; + featureReportDescriptor->setValue((uint8_t*)desc1_val, 2); return featureReportCharacteristic; } @@ -169,14 +186,14 @@ NimBLECharacteristic* NimBLEHIDDevice::featureReport(uint8_t reportID) { * @brief Creates a keyboard boot input report characteristic */ NimBLECharacteristic* NimBLEHIDDevice::bootInput() { - return m_hidService->createCharacteristic((uint16_t) 0x2a22, NIMBLE_PROPERTY::NOTIFY); + return m_hidService->createCharacteristic((uint16_t)0x2a22, NIMBLE_PROPERTY::NOTIFY); } /** * @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); + return m_hidService->createCharacteristic((uint16_t)0x2a32, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::WRITE_NR); } /** diff --git a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEHIDDevice.h b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEHIDDevice.h index 0e8b2828a..6461a4f32 100644 --- a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEHIDDevice.h +++ b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEHIDDevice.h @@ -33,6 +33,7 @@ #define HID_DIGITAL_PEN 0x03C7 #define HID_BARCODE 0x03C8 +#define PNPVersionField(MajorVersion, MinorVersion, PatchVersion) ((MajorVersion << 16) & 0xFF00) | ((MinorVersion << 8) & 0x00F0) | (PatchVersion & 0x000F) /** * @brief A model of a %BLE Human Interface Device. diff --git a/lib/libesp32_div/esp-nimble-cpp/src/NimBLELog.h b/lib/libesp32_div/esp-nimble-cpp/src/NimBLELog.h index 542e3d982..0ccdf4163 100644 --- a/lib/libesp32_div/esp-nimble-cpp/src/NimBLELog.h +++ b/lib/libesp32_div/esp-nimble-cpp/src/NimBLELog.h @@ -14,6 +14,7 @@ #if defined(CONFIG_NIMBLE_CPP_IDF) // using esp-idf # include "esp_log.h" +# include "console/console.h" # ifndef CONFIG_NIMBLE_CPP_LOG_LEVEL # define CONFIG_NIMBLE_CPP_LOG_LEVEL 0 # endif @@ -35,22 +36,6 @@ # define NIMBLE_LOGE(tag, format, ...) \ NIMBLE_CPP_LOG_PRINT(ESP_LOG_ERROR, tag, format, ##__VA_ARGS__) -# define NIMBLE_LOGC(tag, format, ...) \ - NIMBLE_CPP_LOG_PRINT(ESP_LOG_ERROR, tag, format, ##__VA_ARGS__) - -// These defines pollute the global namespace and conflict with Tasmota and basically always turn on `seriallog 3` -#ifdef LOG_LEVEL_DEBUG -#undef LOG_LEVEL_DEBUG -#endif - -#ifdef LOG_LEVEL_INFO -#undef LOG_LEVEL_INFO -#endif - -#ifdef LOG_LEVEL_ERROR -#undef LOG_LEVEL_ERROR -#endif - #else // using Arduino # include "nimble/porting/nimble/include/syscfg/syscfg.h" # include "nimble/console/console.h" @@ -82,12 +67,13 @@ # if CONFIG_NIMBLE_CPP_LOG_LEVEL >= 1 # define NIMBLE_LOGE( tag, format, ... ) console_printf("E %s: " format "\n", tag, ##__VA_ARGS__) -# define NIMBLE_LOGC( tag, format, ... ) console_printf("CRIT %s: " format "\n", tag, ##__VA_ARGS__) # else # define NIMBLE_LOGE( tag, format, ... ) (void)tag -# define NIMBLE_LOGC( tag, format, ... ) (void)tag # endif #endif /* CONFIG_NIMBLE_CPP_IDF */ + +#define NIMBLE_LOGC( tag, format, ... ) console_printf("CRIT %s: " format "\n", tag, ##__VA_ARGS__) + #endif /* CONFIG_BT_ENABLED */ #endif /* MAIN_NIMBLELOG_H_ */ diff --git a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEScan.cpp b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEScan.cpp index c7ee07a0f..b4fe6a921 100644 --- a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEScan.cpp +++ b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEScan.cpp @@ -359,7 +359,7 @@ bool NimBLEScan::start(uint32_t duration, bool is_continue) { case BLE_HS_EOS: case BLE_HS_ECONTROLLER: case BLE_HS_ENOTSYNCED: - NIMBLE_LOGC(LOG_TAG, "Unable to scan - Host Reset"); + NIMBLE_LOGE(LOG_TAG, "Unable to scan - Host Reset"); break; default: @@ -459,7 +459,7 @@ void NimBLEScan::onHostSync() { /** * @brief Start scanning and block until scanning has been completed. - * @param [in] duration The duration in seconds for which to scan. + * @param [in] duration The duration in milliseconds for which to scan. * @param [in] is_continue Set to true to save previous scan results, false to clear them. * @return The scan results. */ diff --git a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEServer.cpp b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEServer.cpp index daa5f2359..15f933c3f 100644 --- a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEServer.cpp +++ b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEServer.cpp @@ -27,6 +27,11 @@ #include "nimble/nimble/host/services/gatt/include/services/gatt/ble_svc_gatt.h" #endif +#include + +#define NIMBLE_SERVER_GET_PEER_NAME_ON_CONNECT_CB 0 +#define NIMBLE_SERVER_GET_PEER_NAME_ON_AUTH_CB 1 + static const char* LOG_TAG = "NimBLEServer"; static NimBLEServerCallbacks defaultCallbacks; @@ -47,6 +52,7 @@ NimBLEServer::NimBLEServer() { #endif m_svcChanged = false; m_deleteCallbacks = true; + m_getPeerNameOnConnect = false; } // NimBLEServer @@ -186,9 +192,8 @@ void NimBLEServer::start() { int rc = ble_gatts_start(); if (rc != 0) { - NIMBLE_LOGE(LOG_TAG, "ble_gatts_start; rc=%d, %s", rc, - NimBLEUtils::returnCodeToString(rc)); - abort(); + NIMBLE_LOGE(LOG_TAG, "ble_gatts_start; rc=%d, %s", rc, NimBLEUtils::returnCodeToString(rc)); + return; } #if CONFIG_NIMBLE_CPP_LOG_LEVEL >= 4 @@ -215,7 +220,9 @@ void NimBLEServer::start() { if(svc->m_removed == 0) { rc = ble_gatts_find_svc(&svc->getUUID().getNative()->u, &svc->m_handle); if(rc != 0) { - abort(); + NIMBLE_LOGW(LOG_TAG, "GATT Server started without service: %s, Service %s", + svc->getUUID().toString().c_str(), svc->isStarted() ? "missing" : "not started"); + continue; // Skip this service as it was not started } } @@ -252,6 +259,15 @@ int NimBLEServer::disconnect(uint16_t connId, uint8_t reason) { return rc; } // disconnect +/** + * @brief Disconnect the specified client with optional reason. + * @param [in] connInfo Connection of the client to disconnect. + * @param [in] reason code for disconnecting. + * @return NimBLE host return code. + */ +int NimBLEServer::disconnect(const NimBLEConnInfo &connInfo, uint8_t reason) { + return disconnect(connInfo.getConnHandle(), reason); +} // disconnect #if !CONFIG_BT_NIMBLE_EXT_ADV || defined(_DOXYGEN_) /** @@ -263,6 +279,15 @@ void NimBLEServer::advertiseOnDisconnect(bool aod) { } // advertiseOnDisconnect #endif +/** + * @brief Set the server to automatically read the name from the connected peer before + * the onConnect callback is called and enables the override callback with name parameter. + * @param [in] enable Enable reading the connected peer name upon connection. + */ +void NimBLEServer::getPeerNameOnConnect(bool enable) { + m_getPeerNameOnConnect = enable; +} // getPeerNameOnConnect + /** * @brief Return the number of connected clients. * @return The number of connected clients. @@ -328,6 +353,113 @@ NimBLEConnInfo NimBLEServer::getPeerIDInfo(uint16_t id) { return peerInfo; } // getPeerIDInfo +/** + * @brief Callback that is called after reading from the peer name characteristic. + * @details This will check the task pointer in the task data struct to determine + * the action to take once the name has been read. If there is a task waiting then + * it will be woken, if not, the the RC value is checked to determine which callback + * should be called. + */ +int NimBLEServer::peerNameCB(uint16_t conn_handle, + const struct ble_gatt_error *error, + struct ble_gatt_attr *attr, + void *arg) { + ble_task_data_t *pTaskData = (ble_task_data_t*)arg; + std::string *name = (std::string*)pTaskData->buf; + int rc = error->status; + + if (rc == 0) { + if (attr) { + name->append(OS_MBUF_DATA(attr->om, char*), OS_MBUF_PKTLEN(attr->om)); + return rc; + } + } + + if (rc == BLE_HS_EDONE) { + // No ask means this was read for a callback. + if (pTaskData->task == nullptr) { + NimBLEServer* pServer = (NimBLEServer*)pTaskData->pATT; + NimBLEConnInfo peerInfo{}; + ble_gap_conn_find(conn_handle, &peerInfo.m_desc); + + // Use the rc value as a flag to indicate which callback should be called. + if (pTaskData->rc == NIMBLE_SERVER_GET_PEER_NAME_ON_CONNECT_CB) { + pServer->m_pServerCallbacks->onConnect(pServer, peerInfo, *name); + } else if (pTaskData->rc == NIMBLE_SERVER_GET_PEER_NAME_ON_AUTH_CB) { + pServer->m_pServerCallbacks->onAuthenticationComplete(peerInfo, *name); + } + } + } else { + NIMBLE_LOGE(LOG_TAG, "NimBLEServerPeerNameCB rc=%d; %s", rc, NimBLEUtils::returnCodeToString(rc)); + } + + if (pTaskData->task != nullptr) { + pTaskData->rc = rc; + xTaskNotifyGive(pTaskData->task); + } else { + // If the read was triggered for callback use then these were allocated. + delete name; + delete pTaskData; + } + + return rc; +} + +/** + * @brief Internal method that sends the read command. + */ +std::string NimBLEServer::getPeerNameInternal(uint16_t conn_handle, TaskHandle_t task, int cb_type) { + std::string *buf = new std::string{}; + ble_task_data_t *taskData = new ble_task_data_t{this, task, cb_type, buf}; + ble_uuid16_t uuid {{BLE_UUID_TYPE_16}, BLE_SVC_GAP_CHR_UUID16_DEVICE_NAME}; + int rc = ble_gattc_read_by_uuid(conn_handle, + 1, + 0xffff, + ((ble_uuid_t*)&uuid), + NimBLEServer::peerNameCB, + taskData); + if (rc != 0) { + NIMBLE_LOGE(LOG_TAG, "ble_gattc_read_by_uuid rc=%d, %s", rc, NimBLEUtils::returnCodeToString(rc)); + NimBLEConnInfo peerInfo{}; + ble_gap_conn_find(conn_handle, &peerInfo.m_desc); + if (cb_type == NIMBLE_SERVER_GET_PEER_NAME_ON_CONNECT_CB) { + m_pServerCallbacks->onConnect(this, peerInfo, *buf); + } else if (cb_type == NIMBLE_SERVER_GET_PEER_NAME_ON_AUTH_CB) { + m_pServerCallbacks->onAuthenticationComplete(peerInfo, *buf); + } + delete buf; + delete taskData; + } else if (task != nullptr) { +#ifdef ulTaskNotifyValueClear + // Clear the task notification value to ensure we block + ulTaskNotifyValueClear(task, ULONG_MAX); +#endif + ulTaskNotifyTake(pdTRUE, portMAX_DELAY); + rc = taskData->rc; + std::string name{*(std::string*)taskData->buf}; + delete buf; + delete taskData; + + if (rc != 0 && rc != BLE_HS_EDONE) { + NIMBLE_LOGE(LOG_TAG, "getPeerName rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc)); + } + + return name; + } + // TaskData and name buffer will be deleted in the callback. + return ""; +} + +/** + * @brief Get the name of the connected peer. + * @param connInfo A reference to a NimBLEConnInfo instance to read the name from. + * @returns A string containing the name. + * @note This is a blocking call and should NOT be called from any callbacks! + */ +std::string NimBLEServer::getPeerName(const NimBLEConnInfo& connInfo) { + std::string name = getPeerNameInternal(connInfo.getConnHandle(), xTaskGetCurrentTaskHandle()); + return name; +} /** * @brief Handle a GATT Server Event. @@ -354,16 +486,22 @@ int NimBLEServer::handleGapEvent(struct ble_gap_event *event, void *arg) { #if !CONFIG_BT_NIMBLE_EXT_ADV NimBLEDevice::startAdvertising(); #endif - } - else { - pServer->m_connectedPeersVec.push_back(event->connect.conn_handle); - + } else { rc = ble_gap_conn_find(event->connect.conn_handle, &peerInfo.m_desc); if (rc != 0) { return 0; } - pServer->m_pServerCallbacks->onConnect(pServer, peerInfo); + pServer->m_connectedPeersVec.push_back(event->connect.conn_handle); + + if (pServer->m_getPeerNameOnConnect) { + pServer->getPeerNameInternal(event->connect.conn_handle, + nullptr, + NIMBLE_SERVER_GET_PEER_NAME_ON_CONNECT_CB); + } else { + pServer->m_pServerCallbacks->onConnect(pServer, peerInfo); + } + } return 0; @@ -378,7 +516,7 @@ int NimBLEServer::handleGapEvent(struct ble_gap_event *event, void *arg) { case BLE_HS_EOS: case BLE_HS_ECONTROLLER: case BLE_HS_ENOTSYNCED: - NIMBLE_LOGC(LOG_TAG, "Disconnect - host reset, rc=%d", event->disconnect.reason); + NIMBLE_LOGE(LOG_TAG, "Disconnect - host reset, rc=%d", event->disconnect.reason); NimBLEDevice::onReset(event->disconnect.reason); break; default: @@ -514,10 +652,26 @@ int NimBLEServer::handleGapEvent(struct ble_gap_event *event, void *arg) { return BLE_ATT_ERR_INVALID_HANDLE; } - pServer->m_pServerCallbacks->onAuthenticationComplete(peerInfo); + if (pServer->m_getPeerNameOnConnect) { + pServer->getPeerNameInternal(event->enc_change.conn_handle, + nullptr, + NIMBLE_SERVER_GET_PEER_NAME_ON_AUTH_CB); + } else { + pServer->m_pServerCallbacks->onAuthenticationComplete(peerInfo); + } return 0; } // BLE_GAP_EVENT_ENC_CHANGE + case BLE_GAP_EVENT_IDENTITY_RESOLVED: { + rc = ble_gap_conn_find(event->identity_resolved.conn_handle, &peerInfo.m_desc); + if(rc != 0) { + return BLE_ATT_ERR_INVALID_HANDLE; + } + + pServer->m_pServerCallbacks->onIdentity(peerInfo); + return 0; + } // BLE_GAP_EVENT_IDENTITY_RESOLVED + case BLE_GAP_EVENT_PASSKEY_ACTION: { struct ble_sm_io pkey = {0,0}; @@ -528,19 +682,20 @@ int NimBLEServer::handleGapEvent(struct ble_gap_event *event, void *arg) { // if the (static)passkey is the default, check the callback for custom value // both values default to the same. if(pkey.passkey == 123456) { - pkey.passkey = pServer->m_pServerCallbacks->onPassKeyRequest(); + pkey.passkey = pServer->m_pServerCallbacks->onPassKeyDisplay(); } rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey); NIMBLE_LOGD(LOG_TAG, "BLE_SM_IOACT_DISP; ble_sm_inject_io result: %d", rc); } else if (event->passkey.params.action == BLE_SM_IOACT_NUMCMP) { NIMBLE_LOGD(LOG_TAG, "Passkey on device's display: %" PRIu32, event->passkey.params.numcmp); - pkey.action = event->passkey.params.action; - pkey.numcmp_accept = pServer->m_pServerCallbacks->onConfirmPIN(event->passkey.params.numcmp); - rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey); - NIMBLE_LOGD(LOG_TAG, "BLE_SM_IOACT_NUMCMP; ble_sm_inject_io result: %d", rc); + rc = ble_gap_conn_find(event->passkey.conn_handle, &peerInfo.m_desc); + if(rc != 0) { + return BLE_ATT_ERR_INVALID_HANDLE; + } + pServer->m_pServerCallbacks->onConfirmPIN(peerInfo, event->passkey.params.numcmp); //TODO: Handle out of band pairing } else if (event->passkey.params.action == BLE_SM_IOACT_OOB) { static uint8_t tem_oob[16] = {0}; @@ -551,14 +706,6 @@ int NimBLEServer::handleGapEvent(struct ble_gap_event *event, void *arg) { rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey); NIMBLE_LOGD(LOG_TAG, "BLE_SM_IOACT_OOB; ble_sm_inject_io result: %d", rc); ////////////////////////////////// - } else if (event->passkey.params.action == BLE_SM_IOACT_INPUT) { - NIMBLE_LOGD(LOG_TAG, "Enter the passkey"); - pkey.action = event->passkey.params.action; - pkey.passkey = pServer->m_pServerCallbacks->onPassKeyRequest(); - - rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey); - NIMBLE_LOGD(LOG_TAG, "BLE_SM_IOACT_INPUT; ble_sm_inject_io result: %d", rc); - } else if (event->passkey.params.action == BLE_SM_IOACT_NONE) { NIMBLE_LOGD(LOG_TAG, "No passkey action required"); } @@ -842,6 +989,10 @@ void NimBLEServerCallbacks::onConnect(NimBLEServer* pServer, NimBLEConnInfo& con NIMBLE_LOGD("NimBLEServerCallbacks", "onConnect(): Default"); } // onConnect +void NimBLEServerCallbacks::onConnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo, std::string& name) { + NIMBLE_LOGD("NimBLEServerCallbacks", "onConnect(): Default"); +} // onConnect + void NimBLEServerCallbacks::onDisconnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo, int reason) { NIMBLE_LOGD("NimBLEServerCallbacks", "onDisconnect(): Default"); @@ -851,18 +1002,26 @@ void NimBLEServerCallbacks::onMTUChange(uint16_t MTU, NimBLEConnInfo& connInfo) NIMBLE_LOGD("NimBLEServerCallbacks", "onMTUChange(): Default"); } // onMTUChange -uint32_t NimBLEServerCallbacks::onPassKeyRequest(){ - NIMBLE_LOGD("NimBLEServerCallbacks", "onPassKeyRequest: default: 123456"); +uint32_t NimBLEServerCallbacks::onPassKeyDisplay(){ + NIMBLE_LOGD("NimBLEServerCallbacks", "onPassKeyDisplay: default: 123456"); return 123456; -} //onPassKeyRequest +} //onPassKeyDisplay -void NimBLEServerCallbacks::onAuthenticationComplete(NimBLEConnInfo& connInfo){ +void NimBLEServerCallbacks::onConfirmPIN(const NimBLEConnInfo& connInfo, uint32_t pin){ + NIMBLE_LOGD("NimBLEServerCallbacks", "onConfirmPIN: default: true"); + NimBLEDevice::injectConfirmPIN(connInfo, true); +} // onConfirmPIN + +void NimBLEServerCallbacks::onIdentity(const NimBLEConnInfo& connInfo){ + NIMBLE_LOGD("NimBLEServerCallbacks", "onIdentity: default"); +} // onIdentity + +void NimBLEServerCallbacks::onAuthenticationComplete(const NimBLEConnInfo& connInfo){ NIMBLE_LOGD("NimBLEServerCallbacks", "onAuthenticationComplete: default"); } // onAuthenticationComplete -bool NimBLEServerCallbacks::onConfirmPIN(uint32_t pin){ - NIMBLE_LOGD("NimBLEServerCallbacks", "onConfirmPIN: default: true"); - return true; -} // onConfirmPIN +void NimBLEServerCallbacks::onAuthenticationComplete(const NimBLEConnInfo& connInfo, const std::string& name){ + NIMBLE_LOGD("NimBLEServerCallbacks", "onAuthenticationComplete: default"); +} // onAuthenticationComplete #endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL */ diff --git a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEServer.h b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEServer.h index ea56ed73f..bbb4ebfb9 100644 --- a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEServer.h +++ b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEServer.h @@ -69,6 +69,8 @@ public: NimBLEService* getServiceByHandle(uint16_t handle); int disconnect(uint16_t connID, uint8_t reason = BLE_ERR_REM_USER_CONN_TERM); + int disconnect(const NimBLEConnInfo &connInfo, + uint8_t reason = BLE_ERR_REM_USER_CONN_TERM); void updateConnParams(uint16_t conn_handle, uint16_t minInterval, uint16_t maxInterval, uint16_t latency, uint16_t timeout); @@ -78,6 +80,8 @@ public: NimBLEConnInfo getPeerInfo(size_t index); NimBLEConnInfo getPeerInfo(const NimBLEAddress& address); NimBLEConnInfo getPeerIDInfo(uint16_t id); + std::string getPeerName(const NimBLEConnInfo& connInfo); + void getPeerNameOnConnect(bool enable); #if !CONFIG_BT_NIMBLE_EXT_ADV || defined(_DOXYGEN_) void advertiseOnDisconnect(bool); #endif @@ -98,6 +102,7 @@ private: #if !CONFIG_BT_NIMBLE_EXT_ADV bool m_advertiseOnDisconnect; #endif + bool m_getPeerNameOnConnect; bool m_svcChanged; NimBLEServerCallbacks* m_pServerCallbacks; bool m_deleteCallbacks; @@ -110,10 +115,14 @@ private: std::vector m_notifyChrVec; static int handleGapEvent(struct ble_gap_event *event, void *arg); + static int peerNameCB(uint16_t conn_handle, const struct ble_gatt_error *error, + struct ble_gatt_attr *attr, void *arg); + std::string getPeerNameInternal(uint16_t conn_handle, TaskHandle_t task, int cb_type = -1); void serviceChanged(); void resetGATT(); bool setIndicateWait(uint16_t conn_handle); void clearIndicateWait(uint16_t conn_handle); + }; // NimBLEServer @@ -128,11 +137,21 @@ public: * @brief Handle a client connection. * This is called when a client connects. * @param [in] pServer A pointer to the %BLE server that received the client connection. - * @param [in] connInfo A reference to a NimBLEConnInfo instance with information + * @param [in] connInfo A reference to a NimBLEConnInfo instance with information. * about the peer connection parameters. */ virtual void onConnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo); + /** + * @brief Handle a client connection. + * This is called when a client connects. + * @param [in] pServer A pointer to the %BLE server that received the client connection. + * @param [in] connInfo A reference to a NimBLEConnInfo instance with information. + * @param [in] name The name of the connected peer device. + * about the peer connection parameters. + */ + virtual void onConnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo, std::string& name); + /** * @brief Handle a client disconnection. * This is called when a client discconnects. @@ -152,24 +171,39 @@ public: virtual void onMTUChange(uint16_t MTU, NimBLEConnInfo& connInfo); /** - * @brief Called when a client requests a passkey for pairing. + * @brief Called when a client requests a passkey for pairing (display). * @return The passkey to be sent to the client. */ - virtual uint32_t onPassKeyRequest(); + virtual uint32_t onPassKeyDisplay(); + + /** + * @brief Called when using numeric comparision for pairing. + * @param [in] connInfo A reference to a NimBLEConnInfo instance with information + * Should be passed back to NimBLEDevice::injectConfirmPIN + * @param [in] pin The pin to compare with the client. + */ + virtual void onConfirmPIN(const NimBLEConnInfo& connInfo, uint32_t pin); /** * @brief Called when the pairing procedure is complete. * @param [in] connInfo A reference to a NimBLEConnInfo instance with information * about the peer connection parameters. */ - virtual void onAuthenticationComplete(NimBLEConnInfo& connInfo); + virtual void onAuthenticationComplete(const NimBLEConnInfo& connInfo); /** - * @brief Called when using numeric comparision for pairing. - * @param [in] pin The pin to compare with the client. - * @return True to accept the pin. + * @brief Called when the pairing procedure is complete. + * @param [in] connInfo A reference to a NimBLEConnInfo instance with information + * @param [in] name The name of the connected peer device. + * about the peer connection parameters. */ - virtual bool onConfirmPIN(uint32_t pin); + virtual void onAuthenticationComplete(const NimBLEConnInfo& connInfo, const std::string& name); + + /** + * @brief Called when the peer identity address is resolved. + * @param [in] connInfo A reference to a NimBLEConnInfo instance with information + */ + virtual void onIdentity(const NimBLEConnInfo& connInfo); }; // NimBLEServerCallbacks #endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL */ diff --git a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEService.cpp b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEService.cpp index 02195ace7..7c2decf01 100644 --- a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEService.cpp +++ b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEService.cpp @@ -126,7 +126,7 @@ bool NimBLEService::start() { // Nimble requires an array of services to be sent to the api // Since we are adding 1 at a time we create an array of 2 and set the type // of the second service to 0 to indicate the end of the array. - ble_gatt_svc_def* svc = new ble_gatt_svc_def[2]; + ble_gatt_svc_def* svc = new ble_gatt_svc_def[2]{}; ble_gatt_chr_def* pChr_a = nullptr; ble_gatt_dsc_def* pDsc_a = nullptr; @@ -188,7 +188,7 @@ bool NimBLEService::start() { pChr_a[i].descriptors = NULL; } else { // Must have last descriptor uuid = 0 so we have to create 1 extra - pDsc_a = new ble_gatt_dsc_def[numDscs+1]; + pDsc_a = new ble_gatt_dsc_def[numDscs+1]{}; int d = 0; for(auto dsc_it = (*chr_it)->m_dscVec.begin(); dsc_it != (*chr_it)->m_dscVec.end(); ++dsc_it ) { if((*dsc_it)->m_removed > 0) { @@ -434,4 +434,14 @@ NimBLEServer* NimBLEService::getServer() { return NimBLEDevice::getServer(); }// getServer + +/** + * @brief Checks if the service has been started. + * @return True if the service has been started. + */ +bool NimBLEService::isStarted() { + return m_pSvcDef != nullptr; +} + + #endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL */ diff --git a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEService.h b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEService.h index 21ec1af70..73cbd87be 100644 --- a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEService.h +++ b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEService.h @@ -44,7 +44,7 @@ public: uint16_t getHandle(); std::string toString(); void dump(); - + bool isStarted(); bool start(); NimBLECharacteristic* createCharacteristic(const char* uuid, diff --git a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEUtils.cpp b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEUtils.cpp index 60ea541f2..06e649c09 100644 --- a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEUtils.cpp +++ b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEUtils.cpp @@ -16,45 +16,6 @@ static const char* LOG_TAG = "NimBLEUtils"; - -/** - * @brief A function for checking validity of connection parameters. - * @param [in] params A pointer to the structure containing the parameters to check. - * @return valid == 0 or error code. - */ -int NimBLEUtils::checkConnParams(ble_gap_conn_params* params) { - /* Check connection interval min */ - if ((params->itvl_min < BLE_HCI_CONN_ITVL_MIN) || - (params->itvl_min > BLE_HCI_CONN_ITVL_MAX)) { - return BLE_ERR_INV_HCI_CMD_PARMS; - } - /* Check connection interval max */ - if ((params->itvl_max < BLE_HCI_CONN_ITVL_MIN) || - (params->itvl_max > BLE_HCI_CONN_ITVL_MAX) || - (params->itvl_max < params->itvl_min)) { - return BLE_ERR_INV_HCI_CMD_PARMS; - } - - /* Check connection latency */ - if (params->latency > BLE_HCI_CONN_LATENCY_MAX) { - return BLE_ERR_INV_HCI_CMD_PARMS; - } - - /* Check supervision timeout */ - if ((params->supervision_timeout < BLE_HCI_CONN_SPVN_TIMEOUT_MIN) || - (params->supervision_timeout > BLE_HCI_CONN_SPVN_TIMEOUT_MAX)) { - return BLE_ERR_INV_HCI_CMD_PARMS; - } - - /* Check connection event length */ - if (params->min_ce_len > params->max_ce_len) { - return BLE_ERR_INV_HCI_CMD_PARMS; - } - - return 0; -} - - /** * @brief Converts a return code from the NimBLE stack to a text string. * @param [in] rc The return code to convert. diff --git a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEUtils.h b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEUtils.h index 006d9352f..57d22a0aa 100644 --- a/lib/libesp32_div/esp-nimble-cpp/src/NimBLEUtils.h +++ b/lib/libesp32_div/esp-nimble-cpp/src/NimBLEUtils.h @@ -43,7 +43,6 @@ public: static char* buildHexData(uint8_t* target, const uint8_t* source, uint8_t length); static const char* advTypeToString(uint8_t advType); static const char* returnCodeToString(int rc); - static int checkConnParams(ble_gap_conn_params* params); }; diff --git a/lib/libesp32_div/esp-nimble-cpp/src/nimconfig.h b/lib/libesp32_div/esp-nimble-cpp/src/nimconfig.h index 9c1903123..d96e37cf7 100644 --- a/lib/libesp32_div/esp-nimble-cpp/src/nimconfig.h +++ b/lib/libesp32_div/esp-nimble-cpp/src/nimconfig.h @@ -32,6 +32,18 @@ # endif #endif +#if CONFIG_NIMBLE_CPP_DEBUG_ASSERT_ENABLED && !defined NDEBUG +void nimble_cpp_assert(const char *file, unsigned line) __attribute((weak, noreturn)); +# define NIMBLE_ATT_VAL_FILE (__builtin_strrchr(__FILE__, '/') ? \ + __builtin_strrchr (__FILE__, '/') + 1 : __FILE__) +# define NIMBLE_CPP_DEBUG_ASSERT(cond) \ + if (!(cond)) { \ + nimble_cpp_assert(NIMBLE_ATT_VAL_FILE, __LINE__); \ + } +#else +# define NIMBLE_CPP_DEBUG_ASSERT(cond) (void(0)) +#endif + #endif /* CONFIG_BT_ENABLED */ #ifdef _DOXYGEN_ diff --git a/tasmota/include/xsns_62_esp32_mi.h b/tasmota/include/xsns_62_esp32_mi.h index 9995b2ff1..27b4ad231 100644 --- a/tasmota/include/xsns_62_esp32_mi.h +++ b/tasmota/include/xsns_62_esp32_mi.h @@ -198,6 +198,7 @@ struct { uint32_t runningScan:1; uint32_t updateScan:1; uint32_t deleteScanTask:1; + uint32_t IRKinCfg:1; uint32_t canConnect:1; @@ -401,7 +402,7 @@ const char kMI32DeviceType[] PROGMEM = {"Flora|MJ_HT_V1|LYWSD02|LYWSD03|CGG1|CGD const char kMI32_ConnErrorMsg[] PROGMEM = "no Error|could not connect|did disconnect|got no service|got no characteristic|can not read|can not notify|can not write|did not write|notify time out"; -const char kMI32_BLEInfoMsg[] PROGMEM = "Scan ended|Got Notification|Did connect|Did disconnect|Still connected|Start passive scanning|Start active scanning|Server characteristic set|Server advertisement set|Server scan response set|Server client did connect|Server client did disconnect"; +const char kMI32_BLEInfoMsg[] PROGMEM = "Scan ended|Got Notification|Did connect|Did disconnect|Still connected|Start passive scanning|Start active scanning|Server characteristic set|Server advertisement set|Server scan response set|Server client did connect|Server client did disconnect| Server client did authenticate"; const char kMI32_ButtonMsg[] PROGMEM = "Single|Double|Hold"; //mapping: in Tasmota: 1,2,3 ; for HomeKit and Xiaomi 0,1,2 /*********************************************************************************************\ @@ -444,6 +445,7 @@ BLE_OP_ON_SUBSCRIBE_TO_NOTIFICATIONS_AND_INDICATIONS, BLE_OP_ON_CONNECT, BLE_OP_ON_DISCONNECT, BLE_OP_ON_STATUS, +BLE_OP_ON_AUTHENTICATED }; enum MI32_ConnErrorMsg { @@ -471,7 +473,8 @@ enum MI32_BLEInfoMsg { MI32_SERV_ADVERTISEMENT_ADDED, MI32_SERV_SCANRESPONSE_ADDED, MI32_SERV_CLIENT_CONNECTED, - MI32_SERV_CLIENT_DISCONNECTED + MI32_SERV_CLIENT_DISCONNECTED, + MI32_SERV_CLIENT_AUTHENTICATED }; /*********************************************************************************************\ diff --git a/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_MI32.ino b/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_MI32.ino index b836b7c3b..6db4ec644 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_MI32.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_MI32.ino @@ -208,6 +208,35 @@ extern "C" { be_raisef(vm, "ble_error", "BLE: could not add MAC to watch list"); } + // helper function + NimBLEConnInfo be_BLE_get_ConnInfo(NimBLEClient *device); + NimBLEConnInfo be_BLE_get_ConnInfo(NimBLEClient *device){ + if(!device){ + return NimBLEDevice::getServer()->getPeerInfo(0); + } else { + return NimBLEDevice::getClientList()->front()->getConnInfo(); + } + } + + // from esp-nimble/ble_sm.c + int ble_sm_read_bond(uint16_t conn_handle, struct ble_store_value_sec *out_bond) + { + struct ble_store_key_sec key_sec; + struct ble_gap_conn_desc desc; + int rc; + + rc = ble_gap_conn_find(conn_handle, &desc); + if (rc != 0) { + return rc; + } + + memset(&key_sec, 0, sizeof key_sec); + key_sec.peer_addr = desc.peer_id_addr; + + rc = ble_store_read_peer_sec(&key_sec, out_bond); + return rc; + } + // BLE.info(void) -> map int32_t be_BLE_info(struct bvm *vm); int32_t be_BLE_info(struct bvm *vm) { @@ -225,26 +254,43 @@ extern "C" { #else be_map_insert_int(vm, "version", 4); #endif -#ifdef CONFIG_BT_NIMBLE_PERSIST +// #ifdef CONFIG_BT_NIMBLE_PERSIST be_map_insert_int(vm, "bonds", NimBLEDevice::getNumBonds()); -#else - be_map_insert_nil(vm, "bonds"); -#endif - if(MI32.mode.connected == 1){ - be_pushstring(vm, "connection"); - be_newobject(vm, "map"); - auto _info = NimBLEDevice::getClientList()->front()->getConnInfo(); - be_map_insert_str(vm, "peer_addr", _info.getAddress().toString().c_str()); - be_map_insert_int(vm, "RSSI", NimBLEDevice::getClientList()->front()->getRssi()); - be_map_insert_int(vm, "MTU", _info.getMTU()); - be_map_insert_bool(vm, "bonded", _info.isBonded()); - be_map_insert_bool(vm, "master", _info.isMaster()); - be_map_insert_bool(vm, "encrypted", _info.isEncrypted()); - be_map_insert_bool(vm, "authenticated", _info.isAuthenticated()); +// #else +// be_map_insert_nil(vm, "bonds"); +// #endif + if(MI32.mode.connected == 1 || MI32.ServerTask != nullptr){ + NimBLEClient* _device = nullptr; + if(MI32.mode.connected == 1){ + _device = NimBLEDevice::getClientList()->front(); + } + NimBLEConnInfo _info = be_BLE_get_ConnInfo(_device); + + be_pushstring(vm, "connection"); + be_newobject(vm, "map"); + + be_map_insert_str(vm, "peer_addr", _info.getAddress().toString().c_str()); + be_map_insert_str(vm, "peerID_addr", _info.getIdAddress().toString().c_str()); + if(_device != nullptr) be_map_insert_int(vm, "RSSI", _device->getRssi()); // ESP32 is client + be_map_insert_int(vm, "MTU", _info.getMTU()); + be_map_insert_bool(vm, "bonded", _info.isBonded()); + be_map_insert_bool(vm, "master", _info.isMaster()); + be_map_insert_bool(vm, "encrypted", _info.isEncrypted()); + be_map_insert_bool(vm, "authenticated", _info.isAuthenticated()); + if(_device == nullptr) be_map_insert_str(vm, "name", NimBLEDevice::getServer()->getPeerName(_info).c_str()); // ESP32 is server + + ble_store_value_sec value_sec; + ble_sm_read_bond(_info.getConnHandle(), &value_sec); + if(value_sec.irk_present == 1){ + char IRK[33]; + ToHex_P(value_sec.irk,16,IRK,33); + be_map_insert_str(vm, "IRK",IRK ); + } + + be_pop(vm, 1); + be_data_insert(vm, -3); + be_pop(vm, 2); - be_pop(vm, 1); - be_data_insert(vm, -3); - be_pop(vm, 2); } be_pop(vm, 1); @@ -271,6 +317,9 @@ be_BLE_op: 3 subscribe 4 unsubscribe - maybe later 5 disconnect +6 discover services +7 discover characteristics + 11 read once, then disconnect 12 write once, then disconnect @@ -294,6 +343,7 @@ __response 227 onConnect 228 onDisconnect 229 onStatus +230 onAuthenticated BLE.conn_cb(cb,buffer) diff --git a/tasmota/tasmota_xsns_sensor/xsns_62_esp32_mi.ino b/tasmota/tasmota_xsns_sensor/xsns_62_esp32_mi.ino index c894b8796..b336ec17c 100644 --- a/tasmota/tasmota_xsns_sensor/xsns_62_esp32_mi.ino +++ b/tasmota/tasmota_xsns_sensor/xsns_62_esp32_mi.ino @@ -87,7 +87,7 @@ class MI32SensorCallback : public NimBLEClientCallbacks { MI32.infoMsg = MI32_DID_CONNECT; MI32.mode.willConnect = 0; MI32.mode.connected = 1; - pclient->updateConnParams(8,11,0,1000); + pclient->updateConnParams(8,16,0,1000); } void onDisconnect(NimBLEClient* pclient, int reason) { MI32.mode.connected = 0; @@ -186,6 +186,19 @@ class MI32ServerCallbacks: public NimBLEServerCallbacks { NimBLEDevice::startAdvertising(); #endif }; + void onAuthenticationComplete(const NimBLEConnInfo& connInfo) { + struct{ + BLERingBufferItem_t header; + uint8_t buffer[sizeof(ble_store_value_sec)]; + } item; + item.header.length = sizeof(ble_store_value_sec); + item.header.type = BLE_OP_ON_AUTHENTICATED; + ble_store_value_sec value_sec; + ble_sm_read_bond(connInfo.getConnHandle(), &value_sec); + memcpy(item.buffer,(uint8_t*)&value_sec,sizeof(ble_store_value_sec)); + xRingbufferSend(BLERingBufferQueue, (const void*)&item, sizeof(BLERingBufferItem_t), pdMS_TO_TICKS(1)); + MI32.infoMsg = MI32_SERV_CLIENT_AUTHENTICATED; + } }; class MI32CharacteristicCallbacks: public NimBLECharacteristicCallbacks { @@ -691,6 +704,12 @@ void MI32Init(void) { #endif const std::string name(TasmotaGlobal.hostname); NimBLEDevice::init(name); + #ifdef CONFIG_BT_NIMBLE_NVS_PERSIST + NimBLEDevice::setSecurityAuth(true, true, true); + #else + NimBLEDevice::setSecurityAuth(false, true, true); + #endif + AddLog(LOG_LEVEL_INFO,PSTR("M32: Init BLE device: %s"),TasmotaGlobal.hostname); MI32.mode.init = 1; MI32.mode.readyForNextConnJob = 1; @@ -947,6 +966,9 @@ void MI32loadCfg(){ MI32HexStringToBytes(_pidStr,_pid); uint16_t _pid16 = _pid[0]*256 + _pid[1]; _numberOfDevices = MIBLEgetSensorSlot(_mac,_pid16,0); + if (MIBLEsensors[_numberOfDevices].PID == 0) { // no Xiaomi sensor + MI32.option.handleEveryDevice = 1; // if in config, we assume to handle it + } _error = false; } } @@ -958,6 +980,9 @@ void MI32loadCfg(){ uint8_t *_key = (uint8_t*) malloc(16); MI32HexStringToBytes(_keyStr,_key); MIBLEsensors[_numberOfDevices].key = _key; + if (MIBLEsensors[_numberOfDevices].PID == 0) { // no Xiaomi sensor + MI32.mode.IRKinCfg = 1; // key is treated as IRK for RPA + } } else{ _error = true; @@ -1239,12 +1264,6 @@ bool MI32StartConnectionTask(){ void MI32ConnectionTask(void *pvParameters){ #if !defined(CONFIG_IDF_TARGET_ESP32C3) || !defined(CONFIG_IDF_TARGET_ESP32C6) //needs more testing ... // NimBLEDevice::setOwnAddrType(BLE_OWN_ADDR_RANDOM,false); //seems to be important for i.e. xbox controller, hopefully not breaking other things -#ifdef CONFIG_BT_NIMBLE_NVS_PERSIST - NimBLEDevice::setSecurityAuth(true, true, true); -#else - NimBLEDevice::setSecurityAuth(false, true, true); -#endif - #endif //CONFIG_IDF_TARGET_ESP32C3 MI32.conCtx->error = MI32_CONN_NO_ERROR; if (MI32ConnectActiveSensor()){ @@ -2049,11 +2068,33 @@ void MI32ParseResponse(char *buf, uint16_t bufsize, uint8_t addr[6], int RSSI) { } } +uint16_t MI32checkRPA(uint8_t *addr) { + br_aes_small_cbcenc_keys cbc_ctx; + size_t data_len = 16; + int idx = -1; + for (auto _sensor : MIBLEsensors) { + idx += 1; + if (_sensor.PID != 0) continue; + if (_sensor.key == nullptr) continue; + uint8_t iv[16] = {0}; + uint8_t data[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,addr[0],addr[1],addr[2]}; + br_aes_small_cbcenc_init(&cbc_ctx, _sensor.key, 16); + br_aes_small_cbcenc_run( &cbc_ctx, iv, data, data_len ); + if(data[13] == addr[3] && data[14] == addr[4] && data[15] == addr[5]) { + MIBLEsensors[idx].lastTime = Rtc.local_time; + return idx; + } + } + return 0xff; +} + void MI32HandleEveryDevice(NimBLEAdvertisedDevice* advertisedDevice, uint8_t addr[6], int RSSI) { - if(advertisedDevice->getAddressType() != BLE_ADDR_PUBLIC) { - return; - } - uint16_t _slot = MIBLEgetSensorSlot(addr, 0, 0); + + uint16_t _slot; + if (advertisedDevice->getAddressType() == BLE_ADDR_PUBLIC) { _slot = MIBLEgetSensorSlot(addr, 0, 0);} + else if (advertisedDevice->getAddress().isRpa() && MI32.mode.IRKinCfg == 1) { _slot = MI32checkRPA(addr);} + else {return;} + if(_slot==0xff) { return; }