diff --git a/CMakeLists.txt b/CMakeLists.txt
index 23b1aa5..eb6349f 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -39,6 +39,7 @@ idf_component_register(
"src/NimBLEDevice.cpp"
"src/NimBLEEddystoneTLM.cpp"
"src/NimBLEEddystoneURL.cpp"
+ "src/NimBLEExtAdvertising.cpp"
"src/NimBLEHIDDevice.cpp"
"src/NimBLERemoteCharacteristic.cpp"
"src/NimBLERemoteDescriptor.cpp"
diff --git a/docs/Bluetooth 5 features.md b/docs/Bluetooth 5 features.md
new file mode 100644
index 0000000..c0d03b5
--- /dev/null
+++ b/docs/Bluetooth 5 features.md
@@ -0,0 +1,29 @@
+# Bluetooth 5.x features
+
+## About extended advertising
+Extended advertising allows for much more capability and flexibility.
+
+* Allows for 251 bytes of advertisement data and up to 1650 bytes when chained (configuration dependant) vs 31.
+
+* New PHY's (physical layers) that allow for faster data rate (2M PHY) or long range/slower data rates (CODED PHY) as well as the original 1M PHY.
+
+* New periodic advertising, allowing the scanning device to sync with the advertisements of a beacon. This allows for the scanning device to sleep or perform other tasks before the next expected advertisement is sent, preserving cpu cycles and power (To be implemented).
+
+
+## Enabling extended advertising
+Extended advertising is supported when enabled with the config option `CONFIG_BT_NIMBLE_EXT_ADV` set to a value of 1. This is done in menuconfig under `Component config > Bluetooth > NimBLE options >
+Enable extended advertising`.
+
+When enabled the following will occur:
+* `NimBLEScan::start` method will scan on both the 1M PHY and the coded PHY standards automatically.
+
+* `NimBLEClient::connect` will use the primary PHY the device is listening on, unless specified (see below).
+
+* `NimBLEClient::setConnectPhy` becomes available to specify the PHY's to connect with (default is all).
+
+* `NimBLEAdvertising` is no longer available for use and is replaced by `NimBLEExtAdvertising`. `NimBLEDevice::getAdvertising` will now return an instance of `NimBLEExtAdvertising`.
+
+* `NimBLEAdvertisementData` is no longer available for use and is replaced by `NimBLEExtAdvertisement`. This new class is where everything about the advertisement is configured, including the advertisement intervals and advertisement ended callback.
+
+
+
diff --git a/examples/Bluetooth_5/NimBLE_extended_client/CMakeLists.txt b/examples/Bluetooth_5/NimBLE_extended_client/CMakeLists.txt
new file mode 100644
index 0000000..f46b44a
--- /dev/null
+++ b/examples/Bluetooth_5/NimBLE_extended_client/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 esp32c3 esp32s3)
+project(NimBLE_extended_client)
diff --git a/examples/Bluetooth_5/NimBLE_extended_client/Makefile b/examples/Bluetooth_5/NimBLE_extended_client/Makefile
new file mode 100644
index 0000000..2e4842d
--- /dev/null
+++ b/examples/Bluetooth_5/NimBLE_extended_client/Makefile
@@ -0,0 +1,3 @@
+PROJECT_NAME := NimBLE_extended_client
+
+include $(IDF_PATH)/make/project.mk
diff --git a/examples/Bluetooth_5/NimBLE_extended_client/main/CMakeLists.txt b/examples/Bluetooth_5/NimBLE_extended_client/main/CMakeLists.txt
new file mode 100644
index 0000000..0a5a557
--- /dev/null
+++ b/examples/Bluetooth_5/NimBLE_extended_client/main/CMakeLists.txt
@@ -0,0 +1,4 @@
+set(COMPONENT_SRCS "main.cpp")
+set(COMPONENT_ADD_INCLUDEDIRS ".")
+
+register_component()
diff --git a/examples/Bluetooth_5/NimBLE_extended_client/main/component.mk b/examples/Bluetooth_5/NimBLE_extended_client/main/component.mk
new file mode 100644
index 0000000..a98f634
--- /dev/null
+++ b/examples/Bluetooth_5/NimBLE_extended_client/main/component.mk
@@ -0,0 +1,4 @@
+#
+# "main" pseudo-component makefile.
+#
+# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)
diff --git a/examples/Bluetooth_5/NimBLE_extended_client/main/main.cpp b/examples/Bluetooth_5/NimBLE_extended_client/main/main.cpp
new file mode 100644
index 0000000..56881d0
--- /dev/null
+++ b/examples/Bluetooth_5/NimBLE_extended_client/main/main.cpp
@@ -0,0 +1,169 @@
+
+/** NimBLE Extended Client Demo:
+ *
+ * Demonstrates the Bluetooth 5.x client capabilities.
+ *
+ * Created: on April 2 2022
+ * Author: H2zero
+ *
+*/
+#include
+
+extern "C" void app_main(void);
+
+void scanEndedCB(NimBLEScanResults results);
+
+#define SERVICE_UUID "ABCD"
+#define CHARACTERISTIC_UUID "1234"
+
+static NimBLEAdvertisedDevice* advDevice;
+static bool doConnect = false;
+static uint32_t scanTime = 10; /* 0 = scan forever */
+
+/* Define the PHY's to use when connecting to peer devices, can be 1, 2, or all 3 (default).*/
+static uint8_t connectPhys = BLE_GAP_LE_PHY_CODED_MASK | BLE_GAP_LE_PHY_1M_MASK /*| BLE_GAP_LE_PHY_2M_MASK */ ;
+
+/* Define a class to handle the callbacks for client connection events */
+class ClientCallbacks : public NimBLEClientCallbacks {
+ void onConnect(NimBLEClient* pClient) {
+ printf("Connected\n");
+ };
+
+ void onDisconnect(NimBLEClient* pClient) {
+ printf("%s Disconnected - Starting scan\n", pClient->getPeerAddress().toString().c_str());
+ NimBLEDevice::getScan()->start(scanTime, scanEndedCB);
+ };
+};
+
+
+/* Define a class to handle the callbacks when advertisements are received */
+class AdvertisedDeviceCallbacks: public NimBLEAdvertisedDeviceCallbacks {
+
+ void onResult(NimBLEAdvertisedDevice* advertisedDevice) {
+ printf("Advertised Device found: %s\n", advertisedDevice->toString().c_str());
+ if(advertisedDevice->isAdvertisingService(NimBLEUUID("ABCD")))
+ {
+ printf("Found Our Service\n");
+ /* Ready to connect now */
+ doConnect = true;
+ /* Save the device reference in a global for the client to use*/
+ advDevice = advertisedDevice;
+ /* stop scan before connecting */
+ NimBLEDevice::getScan()->stop();
+ }
+ };
+};
+
+
+/* Callback to process the results of the last scan or restart it */
+void scanEndedCB(NimBLEScanResults results){
+ printf("Scan Ended\n");
+ if (!doConnect) { /* Don't start the scan while connecting */
+ NimBLEDevice::getScan()->start(scanTime, scanEndedCB);
+ }
+}
+
+
+/* Handles the provisioning of clients and connects / interfaces with the server */
+bool connectToServer() {
+ NimBLEClient* pClient = nullptr;
+
+ pClient = NimBLEDevice::createClient();
+ pClient->setClientCallbacks(new ClientCallbacks, false);
+
+ /* Set the PHY's to use for this connection. This is a bitmask that represents the PHY's:
+ * * 0x01 BLE_GAP_LE_PHY_1M_MASK
+ * * 0x02 BLE_GAP_LE_PHY_2M_MASK
+ * * 0x04 BLE_GAP_LE_PHY_CODED_MASK
+ * Combine these with OR ("|"), eg BLE_GAP_LE_PHY_1M_MASK | BLE_GAP_LE_PHY_2M_MASK | BLE_GAP_LE_PHY_CODED_MASK;
+ */
+ pClient->setConnectPhy(connectPhys);
+
+ /** Set how long we are willing to wait for the connection to complete (seconds), default is 30. */
+ pClient->setConnectTimeout(10);
+
+ if (!pClient->connect(advDevice)) {
+ /* Created a client but failed to connect, don't need to keep it as it has no data */
+ NimBLEDevice::deleteClient(pClient);
+ printf("Failed to connect, deleted client\n");
+ return false;
+ }
+
+ printf("Connected to: %s RSSI: %d\n",
+ pClient->getPeerAddress().toString().c_str(),
+ pClient->getRssi());
+
+ /* Now we can read/write/subscribe the charateristics of the services we are interested in */
+ NimBLERemoteService* pSvc = nullptr;
+ NimBLERemoteCharacteristic* pChr = nullptr;
+
+ pSvc = pClient->getService(SERVICE_UUID);
+
+ if (pSvc) {
+ pChr = pSvc->getCharacteristic(CHARACTERISTIC_UUID);
+
+ if (pChr) {
+ // Read the value of the characteristic.
+ if (pChr->canRead()) {
+ std::string value = pChr->readValue();
+ printf("Characteristic value: %s\n", value.c_str());
+ }
+ }
+
+ } else {
+ printf("ABCD service not found.\n");
+ }
+
+ NimBLEDevice::deleteClient(pClient);
+ printf("Done with this device!\n");
+ return true;
+}
+
+void connectTask (void * parameter){
+ /* Loop here until we find a device we want to connect to */
+ for (;;) {
+ if (doConnect) {
+ /* Found a device we want to connect to, do it now */
+ if (connectToServer()) {
+ printf("Success!, scanning for more!\n");
+ } else {
+ printf("Failed to connect, starting scan\n");
+ }
+
+ doConnect = false;
+ NimBLEDevice::getScan()->start(scanTime, scanEndedCB);
+ }
+ vTaskDelay(pdMS_TO_TICKS(10));
+ }
+
+ vTaskDelete(NULL);
+}
+
+void app_main (void) {
+ printf("Starting NimBLE Client\n");
+ /* Create a task to handle connecting to peers */
+ xTaskCreate(connectTask, "connectTask", 5000, NULL, 1, NULL);
+
+ /* Initialize NimBLE, no device name specified as we are not advertising */
+ NimBLEDevice::init("");
+ NimBLEScan* pScan = NimBLEDevice::getScan();
+
+ /* create a callback that gets called when advertisers are found */
+ pScan->setAdvertisedDeviceCallbacks(new AdvertisedDeviceCallbacks());
+
+ /* Set scan interval (how often) and window (how long) in milliseconds */
+ pScan->setInterval(97);
+ pScan->setWindow(67);
+
+ /* Active scan will gather scan response data from advertisers
+ * but will use more energy from both devices
+ */
+ pScan->setActiveScan(true);
+
+ /* Start scanning for advertisers for the scan time specified (in seconds) 0 = forever
+ * Optional callback for when scanning stops.
+ */
+ pScan->start(scanTime, scanEndedCB);
+
+ printf("Scanning for peripherals\n");
+}
diff --git a/examples/Bluetooth_5/NimBLE_extended_server/CMakeLists.txt b/examples/Bluetooth_5/NimBLE_extended_server/CMakeLists.txt
new file mode 100644
index 0000000..c58174a
--- /dev/null
+++ b/examples/Bluetooth_5/NimBLE_extended_server/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 esp32c3 esp32s3)
+project(NimBLE_extended_server)
diff --git a/examples/Bluetooth_5/NimBLE_extended_server/Makefile b/examples/Bluetooth_5/NimBLE_extended_server/Makefile
new file mode 100644
index 0000000..a18cf9f
--- /dev/null
+++ b/examples/Bluetooth_5/NimBLE_extended_server/Makefile
@@ -0,0 +1,3 @@
+PROJECT_NAME := NimBLE_extended_server
+
+include $(IDF_PATH)/make/project.mk
diff --git a/examples/Bluetooth_5/NimBLE_extended_server/main/CMakeLists.txt b/examples/Bluetooth_5/NimBLE_extended_server/main/CMakeLists.txt
new file mode 100644
index 0000000..0a5a557
--- /dev/null
+++ b/examples/Bluetooth_5/NimBLE_extended_server/main/CMakeLists.txt
@@ -0,0 +1,4 @@
+set(COMPONENT_SRCS "main.cpp")
+set(COMPONENT_ADD_INCLUDEDIRS ".")
+
+register_component()
diff --git a/examples/Bluetooth_5/NimBLE_extended_server/main/component.mk b/examples/Bluetooth_5/NimBLE_extended_server/main/component.mk
new file mode 100644
index 0000000..a98f634
--- /dev/null
+++ b/examples/Bluetooth_5/NimBLE_extended_server/main/component.mk
@@ -0,0 +1,4 @@
+#
+# "main" pseudo-component makefile.
+#
+# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)
diff --git a/examples/Bluetooth_5/NimBLE_extended_server/main/main.cpp b/examples/Bluetooth_5/NimBLE_extended_server/main/main.cpp
new file mode 100644
index 0000000..096a281
--- /dev/null
+++ b/examples/Bluetooth_5/NimBLE_extended_server/main/main.cpp
@@ -0,0 +1,139 @@
+/** NimBLE Extended Server Demo:
+ *
+ * Demonstrates the Bluetooth 5.x extended advertising capabilities.
+ *
+ * This demo will advertise a long data string on the CODED and 1M Phy's and
+ * starts a server allowing connection over either PHY's. It will advertise for
+ * 5 seconds then sleep for 20 seconds, if a client connects it will sleep once
+ * it has disconnected then repeats.
+ *
+ * Created: on April 2 2022
+ * Author: H2zero
+ *
+*/
+
+#include "NimBLEDevice.h"
+#include "esp_sleep.h"
+
+extern "C" void app_main(void);
+
+#define SERVICE_UUID "ABCD"
+#define CHARACTERISTIC_UUID "1234"
+
+/* Time in milliseconds to advertise */
+static uint32_t advTime = 5000;
+
+/* Time to sleep between advertisements */
+static uint32_t sleepSeconds = 20;
+
+/* Primary PHY used for advertising, can be one of BLE_HCI_LE_PHY_1M or BLE_HCI_LE_PHY_CODED */
+static uint8_t primaryPhy = BLE_HCI_LE_PHY_CODED;
+
+/* Secondary PHY used for advertising and connecting,
+ * can be one of BLE_HCI_LE_PHY_1M, BLE_HCI_LE_PHY_2M or BLE_HCI_LE_PHY_CODED
+ */
+static uint8_t secondaryPhy = BLE_HCI_LE_PHY_1M;
+
+
+/* Handler class for server events */
+class ServerCallbacks: public NimBLEServerCallbacks {
+ void onConnect(NimBLEServer* pServer, ble_gap_conn_desc* desc) {
+ printf("Client connected: %s\n", NimBLEAddress(desc->peer_ota_addr).toString().c_str());
+ };
+
+ void onDisconnect(NimBLEServer* pServer) {
+ printf("Client disconnected - sleeping for %u seconds\n", sleepSeconds);
+ esp_deep_sleep_start();
+ };
+};
+
+/* Callback class to handle advertising events */
+class advertisingCallbacks: public NimBLEExtAdvertisingCallbacks {
+ void onStopped(NimBLEExtAdvertising* pAdv, int reason, uint8_t inst_id) {
+ /* Check the reason advertising stopped, don't sleep if client is connecting */
+ printf("Advertising instance %u stopped\n", inst_id);
+ switch (reason) {
+ case 0:
+ printf("Client connecting\n");
+ return;
+ case BLE_HS_ETIMEOUT:
+ printf("Time expired - sleeping for %u seconds\n", sleepSeconds);
+ break;
+ default:
+ break;
+ }
+
+ esp_deep_sleep_start();
+ }
+};
+
+void app_main (void) {
+ NimBLEDevice::init("Extended advertiser");
+
+ /* Create the server and add the services/characteristics/descriptors */
+ NimBLEServer *pServer = NimBLEDevice::createServer();
+ pServer->setCallbacks(new ServerCallbacks);
+
+ NimBLEService *pService = pServer->createService(SERVICE_UUID);
+ NimBLECharacteristic *pCharacteristic = pService->createCharacteristic(CHARACTERISTIC_UUID,
+ NIMBLE_PROPERTY::READ |
+ NIMBLE_PROPERTY::WRITE |
+ NIMBLE_PROPERTY::NOTIFY);
+
+ pCharacteristic->setValue("Hello World");
+
+ /* Start the services */
+ pService->start();
+
+ /*
+ * Create an extended advertisement with the instance ID 0 and set the PHY's.
+ * Multiple instances can be added as long as the instance ID is incremented.
+ */
+ NimBLEExtAdvertisement extAdv(primaryPhy, secondaryPhy);
+
+ /* Set the advertisement as connectable */
+ extAdv.setConnectable(true);
+
+ /* As per Bluetooth specification, extended advertising cannot be both scannable and connectable */
+ extAdv.setScannable(false); // The default is false, set here for demonstration.
+
+ /* Extended advertising allows for 251 bytes (minus header bytes ~20) in a single advertisement or up to 1650 if chained */
+ extAdv.setServiceData(NimBLEUUID(SERVICE_UUID), std::string("Extended Advertising Demo.\r\n"
+ "Extended advertising allows for "
+ "251 bytes of data in a single advertisement,\r\n"
+ "or up to 1650 bytes with chaining.\r\n"
+ "This example message is 226 bytes long "
+ "and is using CODED_PHY for long range."));
+
+ extAdv.setCompleteServices16({NimBLEUUID(SERVICE_UUID)});
+
+ /* When extended advertising is enabled `NimBLEDevice::getAdvertising` returns a pointer to `NimBLEExtAdvertising */
+ NimBLEExtAdvertising* pAdvertising = NimBLEDevice::getAdvertising();
+
+ /* Set the callbacks for advertising events */
+ pAdvertising->setCallbacks(new advertisingCallbacks);
+
+ /*
+ * NimBLEExtAdvertising::setInstanceData takes the instance ID and
+ * a reference to a `NimBLEExtAdvertisement` object. This sets the data
+ * that will be advertised for this instance ID, returns true if successful.
+ *
+ * Note: It is safe to create the advertisement as a local variable if setInstanceData
+ * is called before exiting the code block as the data will be copied.
+ */
+ if (pAdvertising->setInstanceData(0, extAdv)) {
+ /*
+ * `NimBLEExtAdvertising::start` takes the advertisement instance ID to start
+ * and a duration in milliseconds or a max number of advertisements to send (or both).
+ */
+ if (pAdvertising->start(0, advTime)) {
+ printf("Started advertising\n");
+ } else {
+ printf("Failed to start advertising\n");
+ }
+ } else {
+ printf("Failed to register advertisment data\n");
+ }
+
+ esp_sleep_enable_timer_wakeup(sleepSeconds * 1000000);
+}
diff --git a/examples/Bluetooth_5/NimBLE_multi_advertiser/CMakeLists.txt b/examples/Bluetooth_5/NimBLE_multi_advertiser/CMakeLists.txt
new file mode 100644
index 0000000..7cfce86
--- /dev/null
+++ b/examples/Bluetooth_5/NimBLE_multi_advertiser/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 esp32c3 esp32s3)
+project(NimBLE_multi_advertiser)
diff --git a/examples/Bluetooth_5/NimBLE_multi_advertiser/Makefile b/examples/Bluetooth_5/NimBLE_multi_advertiser/Makefile
new file mode 100644
index 0000000..501edc9
--- /dev/null
+++ b/examples/Bluetooth_5/NimBLE_multi_advertiser/Makefile
@@ -0,0 +1,3 @@
+PROJECT_NAME := NimBLE_multi_advertiser
+
+include $(IDF_PATH)/make/project.mk
diff --git a/examples/Bluetooth_5/NimBLE_multi_advertiser/main/CMakeLists.txt b/examples/Bluetooth_5/NimBLE_multi_advertiser/main/CMakeLists.txt
new file mode 100644
index 0000000..0a5a557
--- /dev/null
+++ b/examples/Bluetooth_5/NimBLE_multi_advertiser/main/CMakeLists.txt
@@ -0,0 +1,4 @@
+set(COMPONENT_SRCS "main.cpp")
+set(COMPONENT_ADD_INCLUDEDIRS ".")
+
+register_component()
diff --git a/examples/Bluetooth_5/NimBLE_multi_advertiser/main/component.mk b/examples/Bluetooth_5/NimBLE_multi_advertiser/main/component.mk
new file mode 100644
index 0000000..a98f634
--- /dev/null
+++ b/examples/Bluetooth_5/NimBLE_multi_advertiser/main/component.mk
@@ -0,0 +1,4 @@
+#
+# "main" pseudo-component makefile.
+#
+# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)
diff --git a/examples/Bluetooth_5/NimBLE_multi_advertiser/main/main.cpp b/examples/Bluetooth_5/NimBLE_multi_advertiser/main/main.cpp
new file mode 100644
index 0000000..fbbca34
--- /dev/null
+++ b/examples/Bluetooth_5/NimBLE_multi_advertiser/main/main.cpp
@@ -0,0 +1,170 @@
+/** NimBLE Multi Advertiser Demo:
+ *
+ * Demonstrates the Bluetooth 5.x extended advertising capabilities.
+ *
+ * This demo will advertise 2 advertisements, and extended scannable instance
+ * and a connectable legacy instance. They will advertise for 5 seconds then
+ * sleep for 20 seconds. The extended scannable instance will use the scan
+ * request callback to update it's data when a scan response is requested.
+ *
+ * Created: on April 9 2022
+ * Author: H2zero
+ *
+*/
+
+#include "NimBLEDevice.h"
+#include "esp_sleep.h"
+
+extern "C" void app_main(void);
+
+#define SERVICE_UUID "ABCD"
+#define CHARACTERISTIC_UUID "1234"
+
+/* Time in milliseconds to advertise */
+static uint32_t advTime = 5000;
+
+/* Time to sleep between advertisements */
+static uint32_t sleepTime = 20;
+
+/* Primary PHY used for advertising, can be one of BLE_HCI_LE_PHY_1M or BLE_HCI_LE_PHY_CODED */
+static uint8_t primaryPhy = BLE_HCI_LE_PHY_CODED;
+
+/* Secondary PHY used for advertising and connecting,
+ * can be one of BLE_HCI_LE_PHY_1M, BLE_HCI_LE_PHY_2M or BLE_HCI_LE_PHY_CODED
+ */
+static uint8_t secondaryPhy = BLE_HCI_LE_PHY_1M;
+
+
+/* Handler class for server events */
+class ServerCallbacks: public NimBLEServerCallbacks {
+ void onConnect(NimBLEServer* pServer, ble_gap_conn_desc* desc) {
+ printf("Client connected: %s\n", NimBLEAddress(desc->peer_ota_addr).toString().c_str());
+ };
+
+ void onDisconnect(NimBLEServer* pServer) {
+ printf("Client disconnected\n");
+ // if still advertising we won't sleep yet.
+ if (!pServer->getAdvertising()->isAdvertising()) {
+ printf("Sleeping for %u seconds\n", sleepTime);
+ esp_deep_sleep_start();
+ }
+ };
+};
+
+/* Callback class to handle advertising events */
+class advCallbacks: public NimBLEExtAdvertisingCallbacks {
+ void onStopped(NimBLEExtAdvertising* pAdv, int reason, uint8_t inst_id) {
+ /* Check the reason advertising stopped, don't sleep if client is connecting */
+ printf("Advertising instance %u stopped\n", inst_id);
+ switch (reason) {
+ case 0:
+ printf(" client connecting\n");
+ return;
+ case BLE_HS_ETIMEOUT:
+ printf("Time expired - sleeping for %u seconds\n", sleepTime);
+ break;
+ default:
+ break;
+ }
+
+ esp_deep_sleep_start();
+ }
+
+ bool m_updatedSR = false;
+
+ void onScanRequest(NimBLEExtAdvertising* pAdv, uint8_t inst_id, NimBLEAddress addr) {
+ printf("Scan request for instance %u\n", inst_id);
+ // if the data has already been updated we don't need to change it again.
+ if (!m_updatedSR) {
+ printf("Updating scan data\n");
+ NimBLEExtAdvertisement sr;
+ sr.setServiceData(NimBLEUUID(SERVICE_UUID), std::string("Hello from scan response!"));
+ pAdv->setScanResponseData(inst_id, sr);
+ m_updatedSR = true;
+ }
+ }
+};
+
+void app_main (void) {
+ NimBLEDevice::init("Multi advertiser");
+
+ /* Create a server for our legacy advertiser */
+ NimBLEServer *pServer = NimBLEDevice::createServer();
+ pServer->setCallbacks(new ServerCallbacks);
+
+ NimBLEService *pService = pServer->createService(SERVICE_UUID);
+ NimBLECharacteristic *pCharacteristic = pService->createCharacteristic(CHARACTERISTIC_UUID,
+ NIMBLE_PROPERTY::READ |
+ NIMBLE_PROPERTY::WRITE |
+ NIMBLE_PROPERTY::NOTIFY);
+
+ pCharacteristic->setValue("Hello World");
+
+ /* Start the service */
+ pService->start();
+
+ /* Create our multi advertising instances */
+
+ // extended scannable instance advertising on coded and 1m PHY's.
+ NimBLEExtAdvertisement extScannable(primaryPhy, secondaryPhy);
+
+ // Legacy advertising as a connectable device.
+ NimBLEExtAdvertisement legacyConnectable;
+
+ // Optional scan response data.
+ NimBLEExtAdvertisement legacyScanResponse;
+
+ /* As per Bluetooth specification, extended advertising cannot be both scannable and connectable */
+ extScannable.setScannable(true);
+ extScannable.setConnectable(false);
+
+ /* Set the initial data */
+ extScannable.setServiceData(NimBLEUUID(SERVICE_UUID), std::string("Scan me!"));
+
+ /* enable the scan response callback, we will use this to update the data. */
+ extScannable.enableScanRequestCallback(true);
+
+ /* Optional custom address for this advertisment. */
+ legacyConnectable.setAddress(NimBLEAddress("DE:AD:BE:EF:BA:AD"));
+
+ /* Set the advertising data. */
+ legacyConnectable.setName("Legacy");
+ legacyConnectable.setCompleteServices16({NimBLEUUID(SERVICE_UUID)});
+
+ /* Set the legacy and connectable flags. */
+ legacyConnectable.setLegacyAdvertising(true);
+ legacyConnectable.setConnectable(true);
+
+ /* Put some data in the scan response if desired. */
+ legacyScanResponse.setServiceData(NimBLEUUID(SERVICE_UUID), "Legacy SR");
+
+ /* Get the advertising ready */
+ NimBLEExtAdvertising* pAdvertising = NimBLEDevice::getAdvertising();
+
+ /* Set the callbacks to handle advertising events */
+ pAdvertising->setCallbacks(new advCallbacks);
+
+ /* Set instance data.
+ * Up to 5 instances can be used if configured in menuconfig, instance 0 is always available.
+ *
+ * We will set the extended scannable data on instance 0 and the legacy data on instance 1.
+ * Note that the legacy scan response data needs to be set to the same instance (1).
+ */
+ if (pAdvertising->setInstanceData( 0, extScannable ) &&
+ pAdvertising->setInstanceData( 1, legacyConnectable ) &&
+ pAdvertising->setScanResponseData( 1, legacyScanResponse )) {
+ /*
+ * `NimBLEExtAdvertising::start` takes the advertisement instance ID to start
+ * and a duration in milliseconds or a max number of advertisements to send (or both).
+ */
+ if (pAdvertising->start(0, advTime) && pAdvertising->start(1, advTime)) {
+ printf("Started advertising\n");
+ } else {
+ printf("Failed to start advertising\n");
+ }
+ } else {
+ printf("Failed to register advertisment data\n");
+ }
+
+ esp_sleep_enable_timer_wakeup(sleepTime * 1000000);
+}
diff --git a/src/NimBLEAdvertisedDevice.cpp b/src/NimBLEAdvertisedDevice.cpp
index 01dd75d..29c9532 100644
--- a/src/NimBLEAdvertisedDevice.cpp
+++ b/src/NimBLEAdvertisedDevice.cpp
@@ -20,6 +20,8 @@
#include "NimBLEUtils.h"
#include "NimBLELog.h"
+#include
+
static const char* LOG_TAG = "NimBLEAdvertisedDevice";
@@ -69,7 +71,7 @@ uint8_t NimBLEAdvertisedDevice::getAdvType() {
* @return The appearance of the advertised device.
*/
uint16_t NimBLEAdvertisedDevice::getAppearance() {
- uint8_t data_loc = 0;
+ size_t data_loc = 0;
if(findAdvField(BLE_HS_ADV_TYPE_APPEARANCE, 0, &data_loc) > 0) {
ble_hs_adv_field *field = (ble_hs_adv_field *)&m_payload[data_loc];
@@ -87,7 +89,7 @@ uint16_t NimBLEAdvertisedDevice::getAppearance() {
* @return The advertisement interval in 0.625ms units.
*/
uint16_t NimBLEAdvertisedDevice::getAdvInterval() {
- uint8_t data_loc = 0;
+ size_t data_loc = 0;
if(findAdvField(BLE_HS_ADV_TYPE_ADV_ITVL, 0, &data_loc) > 0) {
ble_hs_adv_field *field = (ble_hs_adv_field *)&m_payload[data_loc];
@@ -105,7 +107,7 @@ uint16_t NimBLEAdvertisedDevice::getAdvInterval() {
* @return The preferred min connection interval in 1.25ms units.
*/
uint16_t NimBLEAdvertisedDevice::getMinInterval() {
- uint8_t data_loc = 0;
+ size_t data_loc = 0;
if(findAdvField(BLE_HS_ADV_TYPE_SLAVE_ITVL_RANGE, 0, &data_loc) > 0) {
ble_hs_adv_field *field = (ble_hs_adv_field *)&m_payload[data_loc];
@@ -123,7 +125,7 @@ uint16_t NimBLEAdvertisedDevice::getMinInterval() {
* @return The preferred max connection interval in 1.25ms units.
*/
uint16_t NimBLEAdvertisedDevice::getMaxInterval() {
- uint8_t data_loc = 0;
+ size_t data_loc = 0;
if(findAdvField(BLE_HS_ADV_TYPE_SLAVE_ITVL_RANGE, 0, &data_loc) > 0) {
ble_hs_adv_field *field = (ble_hs_adv_field *)&m_payload[data_loc];
@@ -141,7 +143,7 @@ uint16_t NimBLEAdvertisedDevice::getMaxInterval() {
* @return The manufacturer data of the advertised device.
*/
std::string NimBLEAdvertisedDevice::getManufacturerData() {
- uint8_t data_loc = 0;
+ size_t data_loc = 0;
if(findAdvField(BLE_HS_ADV_TYPE_MFG_DATA, 0, &data_loc) > 0) {
ble_hs_adv_field *field = (ble_hs_adv_field *)&m_payload[data_loc];
@@ -159,7 +161,7 @@ std::string NimBLEAdvertisedDevice::getManufacturerData() {
* @return The URI data.
*/
std::string NimBLEAdvertisedDevice::getURI() {
- uint8_t data_loc = 0;
+ size_t data_loc = 0;
if(findAdvField(BLE_HS_ADV_TYPE_URI, 0, &data_loc) > 0) {
ble_hs_adv_field *field = (ble_hs_adv_field *)&m_payload[data_loc];
@@ -177,7 +179,7 @@ std::string NimBLEAdvertisedDevice::getURI() {
* @return The name of the advertised device.
*/
std::string NimBLEAdvertisedDevice::getName() {
- uint8_t data_loc = 0;
+ size_t data_loc = 0;
if(findAdvField(BLE_HS_ADV_TYPE_COMP_NAME, 0, &data_loc) > 0 ||
findAdvField(BLE_HS_ADV_TYPE_INCOMP_NAME, 0, &data_loc) > 0)
@@ -214,7 +216,7 @@ NimBLEScan* NimBLEAdvertisedDevice::getScan() {
* @brief Get the number of target addresses.
* @return The number of addresses.
*/
-size_t NimBLEAdvertisedDevice::getTargetAddressCount() {
+uint8_t NimBLEAdvertisedDevice::getTargetAddressCount() {
uint8_t count = 0;
count = findAdvField(BLE_HS_ADV_TYPE_PUBLIC_TGT_ADDR);
@@ -232,7 +234,7 @@ size_t NimBLEAdvertisedDevice::getTargetAddressCount() {
NimBLEAddress NimBLEAdvertisedDevice::getTargetAddress(uint8_t index) {
ble_hs_adv_field *field = nullptr;
uint8_t count = 0;
- uint8_t data_loc = 0xFF;
+ size_t data_loc = ULONG_MAX;
index++;
count = findAdvField(BLE_HS_ADV_TYPE_PUBLIC_TGT_ADDR, index, &data_loc);
@@ -242,7 +244,7 @@ NimBLEAddress NimBLEAdvertisedDevice::getTargetAddress(uint8_t index) {
count = findAdvField(BLE_HS_ADV_TYPE_RANDOM_TGT_ADDR, index, &data_loc);
}
- if(count > 0 && data_loc != 0xFF) {
+ if(count > 0 && data_loc != ULONG_MAX) {
field = (ble_hs_adv_field *)&m_payload[data_loc];
if(field->length < index * BLE_HS_ADV_PUBLIC_TGT_ADDR_ENTRY_LEN) {
index -= count - field->length / BLE_HS_ADV_PUBLIC_TGT_ADDR_ENTRY_LEN;
@@ -264,9 +266,9 @@ NimBLEAddress NimBLEAdvertisedDevice::getTargetAddress(uint8_t index) {
std::string NimBLEAdvertisedDevice::getServiceData(uint8_t index) {
ble_hs_adv_field *field = nullptr;
uint8_t bytes;
- uint8_t data_loc = findServiceData(index, &bytes);
+ size_t data_loc = findServiceData(index, &bytes);
- if(data_loc != 0xFF) {
+ if(data_loc != ULONG_MAX) {
field = (ble_hs_adv_field *)&m_payload[data_loc];
if(field->length > bytes) {
return std::string((char*)(field->value + bytes), field->length - bytes - 1);
@@ -286,9 +288,9 @@ std::string NimBLEAdvertisedDevice::getServiceData(const NimBLEUUID &uuid) {
ble_hs_adv_field *field = nullptr;
uint8_t bytes;
uint8_t index = 0;
- uint8_t data_loc = findServiceData(index, &bytes);
+ size_t data_loc = findServiceData(index, &bytes);
+ size_t plSize = m_payload.size() - 2;
uint8_t uuidBytes = uuid.bitSize() / 8;
- uint8_t plSize = m_payload.size() - 2;
while(data_loc < plSize) {
field = (ble_hs_adv_field *)&m_payload[data_loc];
@@ -313,9 +315,9 @@ std::string NimBLEAdvertisedDevice::getServiceData(const NimBLEUUID &uuid) {
NimBLEUUID NimBLEAdvertisedDevice::getServiceDataUUID(uint8_t index) {
ble_hs_adv_field *field = nullptr;
uint8_t bytes;
- uint8_t data_loc = findServiceData(index, &bytes);
+ size_t data_loc = findServiceData(index, &bytes);
- if(data_loc != 0xFF) {
+ if(data_loc != ULONG_MAX) {
field = (ble_hs_adv_field *)&m_payload[data_loc];
if(field->length >= bytes) {
return NimBLEUUID(field->value, bytes, false);
@@ -330,10 +332,10 @@ NimBLEUUID NimBLEAdvertisedDevice::getServiceDataUUID(uint8_t index) {
* @brief Find the service data at the index.
* @param [in] index The index of the service data to find.
* @param [in] bytes A pointer to storage for the number of the bytes in the UUID.
- * @return The index in the vector where the data is located, 0xFF if not found.
+ * @return The index in the vector where the data is located, ULONG_MAX if not found.
*/
-uint8_t NimBLEAdvertisedDevice::findServiceData(uint8_t index, uint8_t *bytes) {
- uint8_t data_loc = 0;
+size_t NimBLEAdvertisedDevice::findServiceData(uint8_t index, uint8_t *bytes) {
+ size_t data_loc = 0;
uint8_t found = 0;
*bytes = 0;
@@ -358,7 +360,7 @@ uint8_t NimBLEAdvertisedDevice::findServiceData(uint8_t index, uint8_t *bytes) {
return data_loc;
}
- return 0xFF;
+ return ULONG_MAX;
}
@@ -366,7 +368,7 @@ uint8_t NimBLEAdvertisedDevice::findServiceData(uint8_t index, uint8_t *bytes) {
* @brief Get the count of advertised service data UUIDS
* @return The number of service data UUIDS in the vector.
*/
-size_t NimBLEAdvertisedDevice::getServiceDataCount() {
+uint8_t NimBLEAdvertisedDevice::getServiceDataCount() {
uint8_t count = 0;
count += findAdvField(BLE_HS_ADV_TYPE_SVC_DATA_UUID16);
@@ -384,7 +386,7 @@ size_t NimBLEAdvertisedDevice::getServiceDataCount() {
*/
NimBLEUUID NimBLEAdvertisedDevice::getServiceUUID(uint8_t index) {
uint8_t count = 0;
- uint8_t data_loc = 0;
+ size_t data_loc = 0;
uint8_t uuidBytes = 0;
uint8_t type = BLE_HS_ADV_TYPE_INCOMP_UUIDS16;
ble_hs_adv_field *field = nullptr;
@@ -431,7 +433,7 @@ NimBLEUUID NimBLEAdvertisedDevice::getServiceUUID(uint8_t index) {
* @brief Get the number of services advertised
* @return The count of services in the advertising packet.
*/
-size_t NimBLEAdvertisedDevice::getServiceUUIDCount() {
+uint8_t NimBLEAdvertisedDevice::getServiceUUIDCount() {
uint8_t count = 0;
count += findAdvField(BLE_HS_ADV_TYPE_INCOMP_UUIDS16);
@@ -467,7 +469,7 @@ bool NimBLEAdvertisedDevice::isAdvertisingService(const NimBLEUUID &uuid) {
* @return The TX Power of the advertised device.
*/
int8_t NimBLEAdvertisedDevice::getTXPower() {
- uint8_t data_loc = 0;
+ size_t data_loc = 0;
if(findAdvField(BLE_HS_ADV_TYPE_TX_PWR_LVL, 0, &data_loc) > 0) {
ble_hs_adv_field *field = (ble_hs_adv_field *)&m_payload[data_loc];
@@ -581,17 +583,60 @@ bool NimBLEAdvertisedDevice::haveTXPower() {
} // haveTXPower
-uint8_t NimBLEAdvertisedDevice::findAdvField(uint8_t type, uint8_t index, uint8_t *data_loc) {
- ble_hs_adv_field *field = nullptr;
- uint8_t data = 0;
- uint8_t length = m_payload.size();
- uint8_t count = 0;
+#if CONFIG_BT_NIMBLE_EXT_ADV
+/**
+ * @brief Get the set ID of the extended advertisement.
+ * @return The set ID.
+ */
+uint8_t NimBLEAdvertisedDevice::getSetId() {
+ return m_sid;
+} // getSetId
- if(length < 2) {
+
+/**
+ * @brief Get the primary PHY used by this advertisement.
+ * @return The PHY type, one of:
+ * * BLE_HCI_LE_PHY_1M
+ * * BLE_HCI_LE_PHY_CODED
+ */
+uint8_t NimBLEAdvertisedDevice::getPrimaryPhy() {
+ return m_primPhy;
+} // getPrimaryPhy
+
+
+/**
+ * @brief Get the primary PHY used by this advertisement.
+ * @return The PHY type, one of:
+ * * BLE_HCI_LE_PHY_1M
+ * * BLE_HCI_LE_PHY_2M
+ * * BLE_HCI_LE_PHY_CODED
+ */
+uint8_t NimBLEAdvertisedDevice::getSecondaryPhy() {
+ return m_secPhy;
+} // getSecondaryPhy
+
+
+/**
+ * @brief Get the periodic interval of the advertisement.
+ * @return The periodic advertising interval, 0 if not periodic advertising.
+ */
+uint16_t NimBLEAdvertisedDevice::getPeriodicInterval() {
+ return m_periodicItvl;
+} // getPeriodicInterval
+#endif
+
+
+uint8_t NimBLEAdvertisedDevice::findAdvField(uint8_t type, uint8_t index, size_t * data_loc) {
+ ble_hs_adv_field *field = nullptr;
+ size_t length = m_payload.size();
+ size_t data = 0;
+ uint8_t count = 0;
+
+ if (length < 3) {
return count;
}
- while (length > 1) {
+ while (length > 2) {
field = (ble_hs_adv_field*)&m_payload[data];
if (field->length >= length) {
@@ -599,7 +644,7 @@ uint8_t NimBLEAdvertisedDevice::findAdvField(uint8_t type, uint8_t index, uint8_
}
if (field->type == type) {
- switch(type) {
+ switch (type) {
case BLE_HS_ADV_TYPE_INCOMP_UUIDS16:
case BLE_HS_ADV_TYPE_COMP_UUIDS16:
count += field->length / 2;
@@ -625,8 +670,8 @@ uint8_t NimBLEAdvertisedDevice::findAdvField(uint8_t type, uint8_t index, uint8_
break;
}
- if(data_loc != nullptr) {
- if(index == 0 || count >= index) {
+ if (data_loc != nullptr) {
+ if (index == 0 || count >= index) {
break;
}
}
@@ -636,7 +681,7 @@ uint8_t NimBLEAdvertisedDevice::findAdvField(uint8_t type, uint8_t index, uint8_
data += 1 + field->length;
}
- if(data_loc != nullptr && field != nullptr) {
+ if (data_loc != nullptr && field != nullptr) {
*data_loc = data;
}
@@ -657,8 +702,13 @@ void NimBLEAdvertisedDevice::setAddress(NimBLEAddress address) {
* @brief Set the adFlag for this device.
* @param [in] advType The advertisement flag data from the advertisement.
*/
-void NimBLEAdvertisedDevice::setAdvType(uint8_t advType) {
+void NimBLEAdvertisedDevice::setAdvType(uint8_t advType, bool isLegacyAdv) {
m_advType = advType;
+#if CONFIG_BT_NIMBLE_EXT_ADV
+ m_isLegacyAdv = isLegacyAdv;
+#else
+ (void)isLegacyAdv;
+#endif
} // setAdvType
@@ -703,10 +753,10 @@ std::string NimBLEAdvertisedDevice::toString() {
res += val;
}
- if(haveServiceData()) {
- size_t count = getServiceDataCount();
+ if (haveServiceData()) {
+ uint8_t count = getServiceDataCount();
res += "\nService Data:";
- for(size_t i = 0; i < count; i++) {
+ for(uint8_t i = 0; i < count; i++) {
res += "\nUUID: " + std::string(getServiceDataUUID(i));
res += ", Data: " + getServiceData(i);
}
@@ -781,5 +831,34 @@ size_t NimBLEAdvertisedDevice::getPayloadLength() {
return m_payload.size();
} // getPayloadLength
+
+/**
+ * @brief Check if this device is advertising as connectable.
+ * @return True if the device is connectable.
+ */
+bool NimBLEAdvertisedDevice::isConnectable() {
+#if CONFIG_BT_NIMBLE_EXT_ADV
+ if (m_isLegacyAdv) {
+ return m_advType == BLE_HCI_ADV_RPT_EVTYPE_ADV_IND ||
+ m_advType == BLE_HCI_ADV_RPT_EVTYPE_DIR_IND;
+ }
+#endif
+ return (m_advType & BLE_HCI_ADV_CONN_MASK) ||
+ (m_advType & BLE_HCI_ADV_DIRECT_MASK);
+} // isConnectable
+
+
+/**
+ * @brief Check if this advertisement is a legacy or extended type
+ * @return True if legacy (Bluetooth 4.x), false if extended (bluetooth 5.x).
+ */
+bool NimBLEAdvertisedDevice::isLegacyAdvertisement() {
+#if CONFIG_BT_NIMBLE_EXT_ADV
+ return m_isLegacyAdv;
+# else
+ return true;
+#endif
+} // isLegacyAdvertisement
+
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL */
diff --git a/src/NimBLEAdvertisedDevice.h b/src/NimBLEAdvertisedDevice.h
index 39410e6..772bab9 100644
--- a/src/NimBLEAdvertisedDevice.h
+++ b/src/NimBLEAdvertisedDevice.h
@@ -71,7 +71,7 @@ public:
std::string getName();
int getRSSI();
NimBLEScan* getScan();
- size_t getServiceDataCount();
+ uint8_t getServiceDataCount();
std::string getServiceData(uint8_t index = 0);
std::string getServiceData(const NimBLEUUID &uuid);
@@ -111,9 +111,9 @@ public:
NimBLEUUID getServiceDataUUID(uint8_t index = 0);
NimBLEUUID getServiceUUID(uint8_t index = 0);
- size_t getServiceUUIDCount();
+ uint8_t getServiceUUIDCount();
NimBLEAddress getTargetAddress(uint8_t index = 0);
- size_t getTargetAddressCount();
+ uint8_t getTargetAddressCount();
int8_t getTXPower();
uint8_t* getPayload();
uint8_t getAdvLength();
@@ -133,16 +133,30 @@ public:
bool haveTargetAddress();
bool haveURI();
std::string toString();
+ bool isConnectable();
+ bool isLegacyAdvertisement();
+#if CONFIG_BT_NIMBLE_EXT_ADV
+ uint8_t getSetId();
+ uint8_t getPrimaryPhy();
+ uint8_t getSecondaryPhy();
+ uint16_t getPeriodicInterval();
+#endif
private:
friend class NimBLEScan;
void setAddress(NimBLEAddress address);
- void setAdvType(uint8_t advType);
+ void setAdvType(uint8_t advType, bool isLegacyAdv);
void setPayload(const uint8_t *payload, uint8_t length, bool append);
void setRSSI(int rssi);
- uint8_t findAdvField(uint8_t type, uint8_t index = 0, uint8_t *data_loc = nullptr);
- uint8_t findServiceData(uint8_t index, uint8_t* bytes);
+#if CONFIG_BT_NIMBLE_EXT_ADV
+ void setSetId(uint8_t sid) { m_sid = sid; }
+ void setPrimaryPhy(uint8_t phy) { m_primPhy = phy; }
+ void setSecondaryPhy(uint8_t phy) { m_secPhy = phy; }
+ void setPeriodicInterval(uint16_t itvl) { m_periodicItvl = itvl; }
+#endif
+ uint8_t findAdvField(uint8_t type, uint8_t index = 0, size_t * data_loc = nullptr);
+ size_t findServiceData(uint8_t index, uint8_t* bytes);
NimBLEAddress m_address = NimBLEAddress("");
uint8_t m_advType;
@@ -150,6 +164,13 @@ private:
time_t m_timestamp;
bool m_callbackSent;
uint8_t m_advLength;
+#if CONFIG_BT_NIMBLE_EXT_ADV
+ bool m_isLegacyAdv;
+ uint8_t m_sid;
+ uint8_t m_primPhy;
+ uint8_t m_secPhy;
+ uint16_t m_periodicItvl;
+#endif
std::vector m_payload;
};
diff --git a/src/NimBLEAdvertising.cpp b/src/NimBLEAdvertising.cpp
index a804130..d244f41 100644
--- a/src/NimBLEAdvertising.cpp
+++ b/src/NimBLEAdvertising.cpp
@@ -14,7 +14,9 @@
*
*/
#include "nimconfig.h"
-#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
+#if (defined(CONFIG_BT_ENABLED) && \
+ defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER) && \
+ !CONFIG_BT_NIMBLE_EXT_ADV) || defined(_DOXYGEN_)
#if defined(CONFIG_NIMBLE_CPP_IDF)
#include "services/gap/ble_svc_gap.h"
@@ -412,7 +414,7 @@ bool NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdv
// If already advertising just return
if(ble_gap_adv_active()) {
NIMBLE_LOGW(LOG_TAG, "Advertising already active");
- return false;
+ return true;
}
// Save the duration incase of host reset so we can restart with the same params
@@ -625,7 +627,7 @@ bool NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdv
&m_advParams,
(pServer != nullptr) ? NimBLEServer::handleGapEvent :
NimBLEAdvertising::handleGapEvent,
- (pServer != nullptr) ? (void*)pServer : (void*)this);
+ (void*)this);
#else
rc = ble_gap_adv_start(NimBLEDevice::m_own_addr_type, NULL, duration,
&m_advParams, NimBLEAdvertising::handleGapEvent, this);
@@ -634,6 +636,10 @@ bool NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdv
case 0:
break;
+ case BLE_HS_EALREADY:
+ NIMBLE_LOGI(LOG_TAG, "Advertisement Already active");
+ break;
+
case BLE_HS_EINVAL:
NIMBLE_LOGE(LOG_TAG, "Unable to advertise - Duration too long");
break;
@@ -656,24 +662,26 @@ bool NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdv
}
NIMBLE_LOGD(LOG_TAG, "<< Advertising start");
- return (rc == 0);
+ return (rc == 0 || rc == BLE_HS_EALREADY);
} // start
/**
* @brief Stop advertising.
+ * @return True if advertising stopped successfully.
*/
-void NimBLEAdvertising::stop() {
+bool NimBLEAdvertising::stop() {
NIMBLE_LOGD(LOG_TAG, ">> stop");
int rc = ble_gap_adv_stop();
if (rc != 0 && rc != BLE_HS_EALREADY) {
NIMBLE_LOGE(LOG_TAG, "ble_gap_adv_stop rc=%d %s",
rc, NimBLEUtils::returnCodeToString(rc));
- return;
+ return false;
}
NIMBLE_LOGD(LOG_TAG, "<< stop");
+ return true;
} // stop
@@ -1026,4 +1034,4 @@ std::string NimBLEAdvertisementData::getPayload() {
return m_payload;
} // getPayload
-#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_BROADCASTER */
+#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_BROADCASTER && !CONFIG_BT_NIMBLE_EXT_ADV */
diff --git a/src/NimBLEAdvertising.h b/src/NimBLEAdvertising.h
index 63a21d8..dd72ede 100644
--- a/src/NimBLEAdvertising.h
+++ b/src/NimBLEAdvertising.h
@@ -15,7 +15,9 @@
#ifndef MAIN_BLEADVERTISING_H_
#define MAIN_BLEADVERTISING_H_
#include "nimconfig.h"
-#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
+#if (defined(CONFIG_BT_ENABLED) && \
+ defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER) && \
+ !CONFIG_BT_NIMBLE_EXT_ADV) || defined(_DOXYGEN_)
#if defined(CONFIG_NIMBLE_CPP_IDF)
#include "host/ble_gap.h"
@@ -89,7 +91,7 @@ public:
void addServiceUUID(const char* serviceUUID);
void removeServiceUUID(const NimBLEUUID &serviceUUID);
bool start(uint32_t duration = 0, void (*advCompleteCB)(NimBLEAdvertising *pAdv) = nullptr);
- void stop();
+ bool stop();
void setAppearance(uint16_t appearance);
void setName(const std::string &name);
void setManufacturerData(const std::string &data);
@@ -111,6 +113,7 @@ public:
private:
friend class NimBLEDevice;
+ friend class NimBLEServer;
void onHostSync();
static int handleGapEvent(struct ble_gap_event *event, void *arg);
@@ -134,5 +137,5 @@ private:
std::vector m_uri;
};
-#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_BROADCASTER */
+#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_BROADCASTER && !CONFIG_BT_NIMBLE_EXT_ADV */
#endif /* MAIN_BLEADVERTISING_H_ */
diff --git a/src/NimBLEClient.cpp b/src/NimBLEClient.cpp
index d822732..3619b44 100644
--- a/src/NimBLEClient.cpp
+++ b/src/NimBLEClient.cpp
@@ -65,6 +65,11 @@ NimBLEClient::NimBLEClient(const NimBLEAddress &peerAddress) : m_peerAddress(pee
m_pTaskData = nullptr;
m_connEstablished = false;
m_lastErr = 0;
+#if CONFIG_BT_NIMBLE_EXT_ADV
+ m_phyMask = BLE_GAP_LE_PHY_1M_MASK |
+ BLE_GAP_LE_PHY_2M_MASK |
+ BLE_GAP_LE_PHY_CODED_MASK;
+#endif
m_pConnParams.scan_itvl = 16; // Scan interval in 0.625ms units (NimBLE Default)
m_pConnParams.scan_window = 16; // Scan window in 0.625ms units (NimBLE Default)
@@ -220,9 +225,22 @@ bool NimBLEClient::connect(const NimBLEAddress &address, bool deleteAttibutes) {
* Loop on BLE_HS_EBUSY if the scan hasn't stopped yet.
*/
do {
+#if CONFIG_BT_NIMBLE_EXT_ADV
+ rc = ble_gap_ext_connect(NimBLEDevice::m_own_addr_type,
+ &peerAddr_t,
+ m_connectTimeout,
+ m_phyMask,
+ &m_pConnParams,
+ &m_pConnParams,
+ &m_pConnParams,
+ NimBLEClient::handleGapEvent,
+ this);
+
+#else
rc = ble_gap_connect(NimBLEDevice::m_own_addr_type, &peerAddr_t,
m_connectTimeout, &m_pConnParams,
NimBLEClient::handleGapEvent, this);
+#endif
switch (rc) {
case 0:
break;
@@ -397,6 +415,21 @@ int NimBLEClient::disconnect(uint8_t reason) {
} // disconnect
+#if CONFIG_BT_NIMBLE_EXT_ADV
+/**
+ * @brief Set the PHY types to use when connecting to a server.
+ * @param [in] mask A bitmask indicating what PHYS to connect with.\n
+ * The available bits are:
+ * * 0x01 BLE_GAP_LE_PHY_1M_MASK
+ * * 0x02 BLE_GAP_LE_PHY_2M_MASK
+ * * 0x04 BLE_GAP_LE_PHY_CODED_MASK
+ */
+void NimBLEClient::setConnectPhy(uint8_t mask) {
+ m_phyMask = mask;
+}
+#endif
+
+
/**
* @brief Set the connection paramaters to use when connecting to a server.
* @param [in] minInterval The minimum connection interval in 1.25ms units.
diff --git a/src/NimBLEClient.h b/src/NimBLEClient.h
index 7c93c30..4aed02a 100644
--- a/src/NimBLEClient.h
+++ b/src/NimBLEClient.h
@@ -73,6 +73,9 @@ public:
void discoverAttributes();
NimBLEConnInfo getConnInfo();
int getLastError();
+#if CONFIG_BT_NIMBLE_EXT_ADV
+ void setConnectPhy(uint8_t mask);
+#endif
private:
NimBLEClient(const NimBLEAddress &peerAddress);
@@ -98,6 +101,9 @@ private:
NimBLEClientCallbacks* m_pClientCallbacks;
ble_task_data_t* m_pTaskData;
ble_npl_callout m_dcTimer;
+#if CONFIG_BT_NIMBLE_EXT_ADV
+ uint8_t m_phyMask;
+#endif
std::vector m_servicesVector;
diff --git a/src/NimBLEDevice.cpp b/src/NimBLEDevice.cpp
index 8c205af..1c337b8 100644
--- a/src/NimBLEDevice.cpp
+++ b/src/NimBLEDevice.cpp
@@ -69,7 +69,11 @@ NimBLEServer* NimBLEDevice::m_pServer = nullptr;
uint32_t NimBLEDevice::m_passkey = 123456;
bool NimBLEDevice::m_synced = false;
#if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
+# if CONFIG_BT_NIMBLE_EXT_ADV
+NimBLEExtAdvertising* NimBLEDevice::m_bleAdvertising = nullptr;
+# else
NimBLEAdvertising* NimBLEDevice::m_bleAdvertising = nullptr;
+# endif
#endif
gap_event_handler NimBLEDevice::m_customGapHandler = nullptr;
@@ -114,6 +118,45 @@ uint8_t NimBLEDevice::m_scanFilterMode = CONFIG_BTDM_SCAN_DU
#if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
+# if CONFIG_BT_NIMBLE_EXT_ADV
+/**
+ * @brief Get the instance of the advertising object.
+ * @return A pointer to the advertising object.
+ */
+NimBLEExtAdvertising* NimBLEDevice::getAdvertising() {
+ if(m_bleAdvertising == nullptr) {
+ m_bleAdvertising = new NimBLEExtAdvertising();
+ }
+ return m_bleAdvertising;
+}
+
+
+/**
+ * @brief Convenience function to begin advertising.
+ * @param [in] inst_id The extended advertisement instance ID to start.
+ * @param [in] duration How long to advertise for in milliseconds, 0 = forever (default).
+ * @param [in] max_events Maximum number of advertisement events to send, 0 = no limit (default).
+ * @return True if advertising started successfully.
+ */
+bool NimBLEDevice::startAdvertising(uint8_t inst_id,
+ int duration,
+ int max_events) {
+ return getAdvertising()->start(inst_id, duration, max_events);
+} // startAdvertising
+
+
+/**
+ * @brief Convenience function to stop advertising a data set.
+ * @param [in] inst_id The extended advertisement instance ID to stop advertising.
+ * @return True if advertising stopped successfully.
+ */
+bool NimBLEDevice::stopAdvertising(uint8_t inst_id) {
+ return getAdvertising()->stop(inst_id);
+} // stopAdvertising
+
+# endif
+
+# if !CONFIG_BT_NIMBLE_EXT_ADV || defined(_DOXYGEN_)
/**
* @brief Get the instance of the advertising object.
* @return A pointer to the advertising object.
@@ -128,17 +171,19 @@ NimBLEAdvertising* NimBLEDevice::getAdvertising() {
/**
* @brief Convenience function to begin advertising.
+ * @return True if advertising started successfully.
*/
-void NimBLEDevice::startAdvertising() {
- getAdvertising()->start();
+bool NimBLEDevice::startAdvertising() {
+ return getAdvertising()->start();
} // startAdvertising
-
+# endif
/**
- * @brief Convenience function to stop advertising.
+ * @brief Convenience function to stop all advertising.
+ * @return True if advertising stopped successfully.
*/
-void NimBLEDevice::stopAdvertising() {
- getAdvertising()->stop();
+bool NimBLEDevice::stopAdvertising() {
+ return getAdvertising()->stop();
} // stopAdvertising
#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
diff --git a/src/NimBLEDevice.h b/src/NimBLEDevice.h
index 94ad294..b7e804b 100644
--- a/src/NimBLEDevice.h
+++ b/src/NimBLEDevice.h
@@ -23,7 +23,11 @@
#endif
#if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
-#include "NimBLEAdvertising.h"
+# if CONFIG_BT_NIMBLE_EXT_ADV
+# include "NimBLEExtAdvertising.h"
+# else
+# include "NimBLEAdvertising.h"
+# endif
#endif
#if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
@@ -139,9 +143,18 @@ public:
static void removeIgnored(const NimBLEAddress &address);
#if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
- static NimBLEAdvertising* getAdvertising();
- static void startAdvertising();
- static void stopAdvertising();
+# if CONFIG_BT_NIMBLE_EXT_ADV
+ static NimBLEExtAdvertising* getAdvertising();
+ static bool startAdvertising(uint8_t inst_id,
+ int duration = 0,
+ int max_events = 0);
+ static bool stopAdvertising(uint8_t inst_id);
+ static bool stopAdvertising();
+# else
+ static NimBLEAdvertising* getAdvertising();
+ static bool startAdvertising();
+ static bool stopAdvertising();
+# endif
#endif
#if defined( CONFIG_BT_NIMBLE_ROLE_CENTRAL)
@@ -178,6 +191,10 @@ private:
#if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
friend class NimBLEAdvertising;
+# if CONFIG_BT_NIMBLE_EXT_ADV
+ friend class NimBLEExtAdvertising;
+ friend class NimBLEExtAdvertisement;
+# endif
#endif
static void onReset(int reason);
@@ -194,7 +211,11 @@ private:
#endif
#if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
+# if CONFIG_BT_NIMBLE_EXT_ADV
+ static NimBLEExtAdvertising* m_bleAdvertising;
+# else
static NimBLEAdvertising* m_bleAdvertising;
+# endif
#endif
#if defined( CONFIG_BT_NIMBLE_ROLE_CENTRAL)
diff --git a/src/NimBLEExtAdvertising.cpp b/src/NimBLEExtAdvertising.cpp
new file mode 100644
index 0000000..b488e7f
--- /dev/null
+++ b/src/NimBLEExtAdvertising.cpp
@@ -0,0 +1,870 @@
+/*
+ * NimBLEExtAdvertising.cpp
+ *
+ * Created: on February 6, 2022
+ * Author H2zero
+ */
+
+#include "nimconfig.h"
+#if defined(CONFIG_BT_ENABLED) && \
+ defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER) && \
+ CONFIG_BT_NIMBLE_EXT_ADV
+
+#if defined(CONFIG_NIMBLE_CPP_IDF)
+#include "services/gap/ble_svc_gap.h"
+#else
+#include "nimble/nimble/host/services/gap/include/services/gap/ble_svc_gap.h"
+#endif
+#include "NimBLEExtAdvertising.h"
+#include "NimBLEDevice.h"
+#include "NimBLEServer.h"
+#include "NimBLEUtils.h"
+#include "NimBLELog.h"
+
+static NimBLEExtAdvertisingCallbacks defaultCallbacks;
+static const char* LOG_TAG = "NimBLEExtAdvertising";
+
+
+/**
+ * @brief Destructor: deletes callback instances if requested.
+ */
+NimBLEExtAdvertising::~NimBLEExtAdvertising() {
+ if(m_deleteCallbacks && m_pCallbacks != &defaultCallbacks) {
+ delete m_pCallbacks;
+ }
+}
+
+
+/**
+ * @brief Register the extended advertisement data.
+ * @param [in] inst_id The extended advertisement instance ID to assign to this data.
+ * @param [in] adv The extended advertisement instance with the data to set.
+ * @return True if advertising started successfully.
+ */
+bool NimBLEExtAdvertising::setInstanceData(uint8_t inst_id, NimBLEExtAdvertisement& adv) {
+ adv.m_params.sid = inst_id;
+
+ // Legacy advertising as connectable requires the scannable flag also.
+ if (adv.m_params.legacy_pdu && adv.m_params.connectable) {
+ adv.m_params.scannable = true;
+ }
+
+ // If connectable or not scannable disable the callback for scan response requests
+ if (adv.m_params.connectable || !adv.m_params.scannable) {
+ adv.m_params.scan_req_notif = false;
+ }
+
+#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
+ NimBLEServer* pServer = NimBLEDevice::getServer();
+ if (pServer != nullptr) {
+ if (!pServer->m_gattsStarted) {
+ pServer->start();
+ }
+ }
+
+ int rc = ble_gap_ext_adv_configure(inst_id,
+ &adv.m_params,
+ NULL,
+ (pServer != nullptr) ? NimBLEServer::handleGapEvent :
+ NimBLEExtAdvertising::handleGapEvent,
+ NULL);
+#else
+ int rc = ble_gap_ext_adv_configure(inst_id,
+ &data.m_params,
+ NULL,
+ NimBLEExtAdvertising::handleGapEvent,
+ NULL);
+#endif
+
+ if (rc != 0) {
+ NIMBLE_LOGE(LOG_TAG, "Advertising config error: rc = %d", rc);
+ } else {
+ os_mbuf *buf;
+ buf = os_msys_get_pkthdr(adv.m_payload.size(), 0);
+ if (!buf) {
+ NIMBLE_LOGE(LOG_TAG, "Data buffer allocation failed");
+ return false;
+ }
+
+ rc = os_mbuf_append(buf, &adv.m_payload[0], adv.m_payload.size());
+ if (rc != 0) {
+ NIMBLE_LOGE(LOG_TAG, "Unable to copy data: rc = %d", rc);
+ return false;
+ } else {
+ if (adv.m_params.scannable && !adv.m_params.legacy_pdu) {
+ rc = ble_gap_ext_adv_rsp_set_data(inst_id, buf);
+ } else {
+ rc = ble_gap_ext_adv_set_data(inst_id, buf);
+ }
+
+ if (rc != 0) {
+ NIMBLE_LOGE(LOG_TAG, "Invalid advertisement data: rc = %d", rc);
+ } else {
+ if (adv.m_advAddress != NimBLEAddress("")) {
+ ble_addr_t addr;
+ memcpy(&addr.val, adv.m_advAddress.getNative(), 6);
+ // Custom advertising address must be random.
+ addr.type = BLE_OWN_ADDR_RANDOM;
+ rc = ble_gap_ext_adv_set_addr(inst_id, &addr);
+ }
+
+ if (rc != 0) {
+ NIMBLE_LOGE(LOG_TAG, "Error setting advertisement address: rc = %d", rc);
+ return false;
+ }
+ }
+ }
+ }
+
+ return (rc == 0);
+}
+
+
+/**
+ * @brief Set the scan response data for a legacy advertisement.
+ * @param [in] inst_id The extended advertisement instance ID to assign to this data.
+ * @param [in] lsr A reference to a NimBLEExtAdvertisement that contains the data.
+ */
+bool NimBLEExtAdvertising::setScanResponseData(uint8_t inst_id, NimBLEExtAdvertisement & lsr) {
+ os_mbuf *buf = os_msys_get_pkthdr(lsr.m_payload.size(), 0);
+ if (!buf) {
+ NIMBLE_LOGE(LOG_TAG, "Data buffer allocation failed");
+ return false;
+ }
+
+ int rc = os_mbuf_append(buf, &lsr.m_payload[0], lsr.m_payload.size());
+
+ if (rc != 0) {
+ NIMBLE_LOGE(LOG_TAG, "Unable to copy scan data: rc = %d", rc);
+ return false;
+ } else {
+ rc = ble_gap_ext_adv_rsp_set_data(inst_id, buf);
+ }
+ return (rc == 0);
+}
+
+
+/**
+ * @brief Start extended advertising.
+ * @param [in] inst_id The extended advertisement instance ID to start.
+ * @param [in] duration How long to advertise for in milliseconds, 0 = forever (default).
+ * @param [in] max_events Maximum number of advertisement events to send, 0 = no limit (default).
+ * @return True if advertising started successfully.
+ */
+bool NimBLEExtAdvertising::start(uint8_t inst_id, int duration, int max_events) {
+ NIMBLE_LOGD(LOG_TAG, ">> Extended Advertising start");
+
+ // If Host is not synced we cannot start advertising.
+ if(!NimBLEDevice::m_synced) {
+ NIMBLE_LOGE(LOG_TAG, "Host reset, wait for sync.");
+ return false;
+ }
+
+ int rc = ble_gap_ext_adv_start(inst_id, duration / 10, max_events);
+
+ switch (rc) {
+ case 0:
+ m_advStatus[inst_id] = true;
+ break;
+
+ case BLE_HS_EINVAL:
+ NIMBLE_LOGE(LOG_TAG, "Unable to advertise - Value Error");
+ break;
+
+ case BLE_HS_EALREADY:
+ NIMBLE_LOGI(LOG_TAG, "Advertisement Already active");
+ break;
+
+ case BLE_HS_ETIMEOUT_HCI:
+ case BLE_HS_EOS:
+ case BLE_HS_ECONTROLLER:
+ case BLE_HS_ENOTSYNCED:
+ NIMBLE_LOGE(LOG_TAG, "Unable to advertise - Host Reset");
+ break;
+
+ default:
+ NIMBLE_LOGE(LOG_TAG, "Error enabling advertising; rc=%d, %s",
+ rc, NimBLEUtils::returnCodeToString(rc));
+ break;
+ }
+
+ NIMBLE_LOGD(LOG_TAG, "<< Extended Advertising start");
+ return (rc == 0 || rc == BLE_HS_EALREADY);
+} // start
+
+
+/**
+ * @brief Stop and remove this instance data from the advertisement set.
+ * @param [in] inst_id The extended advertisement instance to stop advertising.
+ * @return True if successful.
+ */
+bool NimBLEExtAdvertising::removeInstance(uint8_t inst_id) {
+ if (stop(inst_id)) {
+ int rc = ble_gap_ext_adv_remove(inst_id);
+ if (rc != 0 && rc != BLE_HS_EALREADY) {
+ NIMBLE_LOGE(LOG_TAG, "ble_gap_ext_adv_remove rc = %d %s",
+ rc, NimBLEUtils::returnCodeToString(rc));
+ return false;
+ }
+ return true;
+ }
+
+ return false;
+} // removeInstance
+
+
+/**
+ * @brief Stop and remove all advertising instance data.
+ * @return True if successful.
+ */
+bool NimBLEExtAdvertising::removeAll() {
+ if (stop()) {
+ int rc = ble_gap_ext_adv_clear();
+ if (rc == 0 || rc == BLE_HS_EALREADY) {
+ return true;
+ } else {
+ NIMBLE_LOGE(LOG_TAG, "ble_gap_ext_adv_clear rc = %d %s",
+ rc, NimBLEUtils::returnCodeToString(rc));
+ }
+ }
+
+ return false;
+} // removeAll
+
+
+/**
+ * @brief Stop advertising this instance data.
+ * @param [in] inst_id The extended advertisement instance to stop advertising.
+ * @return True if successful.
+ */
+bool NimBLEExtAdvertising::stop(uint8_t inst_id) {
+ int rc = ble_gap_ext_adv_stop(inst_id);
+ if (rc != 0 && rc != BLE_HS_EALREADY) {
+ NIMBLE_LOGE(LOG_TAG, "ble_gap_ext_adv_stop rc = %d %s",
+ rc, NimBLEUtils::returnCodeToString(rc));
+ return false;
+ }
+
+ m_advStatus[inst_id] = false;
+ return true;
+} // stop
+
+
+/**
+ * @brief Stop all advertisements.
+ * @return True if successful.
+ */
+bool NimBLEExtAdvertising::stop() {
+ int rc = ble_gap_ext_adv_clear();
+ if (rc != 0 && rc != BLE_HS_EALREADY) {
+ NIMBLE_LOGE(LOG_TAG, "ble_gap_ext_adv_stop rc = %d %s",
+ rc, NimBLEUtils::returnCodeToString(rc));
+ return false;
+ }
+
+ for(auto it : m_advStatus) {
+ it = false;
+ }
+
+ return true;
+} // stop
+
+
+/**
+ * @brief Set a callback to call when the advertisement stops.
+ * @param [in] pCallbacks A pointer to a callback to be invoked when an advertisment stops.
+ * @param [in] deleteCallbacks if true callback class will be deleted when advertising is destructed.
+ */
+void NimBLEExtAdvertising::setCallbacks(NimBLEExtAdvertisingCallbacks* pCallbacks,
+ bool deleteCallbacks) {
+ if (pCallbacks != nullptr){
+ m_pCallbacks = pCallbacks;
+ m_deleteCallbacks = deleteCallbacks;
+ } else {
+ m_pCallbacks = &defaultCallbacks;
+ }
+} // setCallbacks
+
+
+/**
+ * @brief Check if currently advertising.
+ * @param [in] inst_id The instance ID of the advertised data to get the status of.
+ * @return True if advertising is active.
+ */
+bool NimBLEExtAdvertising::isActive(uint8_t inst_id) {
+ return m_advStatus[inst_id];
+} // isAdvertising
+
+
+/**
+ * @brief Check if any instances are currently advertising.
+ * @return True if any instance is active.
+ */
+bool NimBLEExtAdvertising::isAdvertising() {
+ for (auto it : m_advStatus) {
+ if (it) {
+ return true;
+ }
+ }
+ return false;
+} // isAdvertising
+
+
+/*
+ * Host reset seems to clear advertising data,
+ * we need clear the flag so it reloads it.
+ */
+void NimBLEExtAdvertising::onHostSync() {
+ NIMBLE_LOGD(LOG_TAG, "Host re-synced");
+ for(auto it : m_advStatus) {
+ it = false;
+ }
+} // onHostSync
+
+
+/**
+ * @brief Handler for gap events when not using peripheral role.
+ * @param [in] event the event data.
+ * @param [in] arg pointer to the advertising instance.
+ */
+/*STATIC*/
+int NimBLEExtAdvertising::handleGapEvent(struct ble_gap_event *event, void *arg) {
+ (void)arg;
+ NimBLEExtAdvertising* pAdv = NimBLEDevice::getAdvertising();
+
+ switch (event->type) {
+ case BLE_GAP_EVENT_ADV_COMPLETE: {
+ switch (event->adv_complete.reason) {
+ // Don't call the callback if host reset, we want to
+ // preserve the active flag until re-sync to restart advertising.
+ case BLE_HS_ETIMEOUT_HCI:
+ case BLE_HS_EOS:
+ case BLE_HS_ECONTROLLER:
+ case BLE_HS_ENOTSYNCED:
+ NIMBLE_LOGC(LOG_TAG, "host reset, rc = %d", event->adv_complete.reason);
+ NimBLEDevice::onReset(event->adv_complete.reason);
+ return 0;
+ default:
+ break;
+ }
+ pAdv->m_advStatus[event->adv_complete.instance] = false;
+ pAdv->m_pCallbacks->onStopped(pAdv, event->adv_complete.reason,
+ event->adv_complete.instance);
+ break;
+ }
+
+ case BLE_GAP_EVENT_SCAN_REQ_RCVD: {
+ pAdv->m_pCallbacks->onScanRequest(pAdv, event->scan_req_rcvd.instance,
+ NimBLEAddress(event->scan_req_rcvd.scan_addr));
+ break;
+ }
+ }
+
+ return 0;
+} // handleGapEvent
+
+
+/** Default callback handlers */
+void NimBLEExtAdvertisingCallbacks::onStopped(NimBLEExtAdvertising *pAdv,
+ int reason, uint8_t inst_id) {
+ NIMBLE_LOGD("NimBLEExtAdvertisingCallbacks", "onStopped: Default");
+} // onStopped
+
+
+void NimBLEExtAdvertisingCallbacks::onScanRequest(NimBLEExtAdvertising *pAdv,
+ uint8_t inst_id, NimBLEAddress addr) {
+ NIMBLE_LOGD("NimBLEExtAdvertisingCallbacks", "onScanRequest: Default");
+} // onScanRequest
+
+
+/**
+ * @brief Construct a BLE extended advertisement.
+ * @param [in] priPhy The primary Phy to advertise on, can be one of:
+ * * BLE_HCI_LE_PHY_1M
+ * * BLE_HCI_LE_PHY_CODED
+ * @param [in] secPhy The secondary Phy to advertise on, can be one of:
+ * * BLE_HCI_LE_PHY_1M
+ * * BLE_HCI_LE_PHY_2M
+ * * BLE_HCI_LE_PHY_CODED
+ */
+NimBLEExtAdvertisement::NimBLEExtAdvertisement(uint8_t priPhy, uint8_t secPhy)
+: m_advAddress("")
+{
+ memset (&m_params, 0, sizeof(m_params));
+ m_params.own_addr_type = NimBLEDevice::m_own_addr_type;
+ m_params.primary_phy = priPhy;
+ m_params.secondary_phy = secPhy;
+ m_params.tx_power = 127;
+} // NimBLEExtAdvertisement
+
+
+/**
+ * @brief Sets wether the advertisement should use legacy (BLE 4.0, 31 bytes max) advertising.
+ * @param [in] val true = using legacy advertising.
+ */
+void NimBLEExtAdvertisement::setLegacyAdvertising(bool val) {
+ m_params.legacy_pdu = val;
+} // setLegacyAdvertising
+
+
+/**
+ * @brief Sets wether the advertisement has scan response data available.
+ * @param [in] val true = scan response is available.
+ */
+void NimBLEExtAdvertisement::setScannable(bool val) {
+ m_params.scannable = val;
+} // setScannable
+
+
+
+/**
+ * @brief Sets the transmission power level for this advertisement.
+ * @param [in] dbm the transmission power to use in dbm.
+ * @details The allowable value range depends on device hardware. \n
+ * The ESP32C3 and ESP32S3 have a range of -27 to +18.
+ */
+void NimBLEExtAdvertisement::setTxPower(int8_t dbm) {
+ m_params.tx_power = dbm;
+}
+
+
+/**
+ * @brief Sets wether this advertisement should advertise as a connectable device.
+ * @param [in] val True = connectable.
+ */
+void NimBLEExtAdvertisement::setConnectable(bool val) {
+#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
+ m_params.connectable = val;
+#endif
+} // setConnectable
+
+
+/**
+ * @brief Set the address to use for this advertisement.
+ * @param [in] addr The address to use.
+ */
+void NimBLEExtAdvertisement::setAddress(const NimBLEAddress & addr) {
+ m_advAddress = addr;
+ // Must use random address type.
+ m_params.own_addr_type = BLE_OWN_ADDR_RANDOM;
+}
+
+
+/**
+ * @brief Sets The primary channels to advertise on.
+ * @param [in] ch37 Advertise on channel 37.
+ * @param [in] ch38 Advertise on channel 38.
+ * @param [in] ch39 Advertise on channel 39.
+ * @details This will set a bitmask using the input parameters to allow different \n
+ * combinations. If all inputs are false then all 3 channels will be used.
+ */
+void NimBLEExtAdvertisement::setPrimaryChannels(bool ch37, bool ch38, bool ch39) {
+ m_params.channel_map = (ch37 | (ch38 << 1) | (ch39 << 2));
+} // setPrimaryChannels
+
+
+/**
+ * @brief Set the filtering for the scan filter.
+ * @param [in] scanRequestWhitelistOnly If true, only allow scan requests from those on the white list.
+ * @param [in] connectWhitelistOnly If true, only allow connections from those on the white list.
+ */
+void NimBLEExtAdvertisement::setScanFilter(bool scanRequestWhitelistOnly, bool connectWhitelistOnly) {
+ if (!scanRequestWhitelistOnly && !connectWhitelistOnly) {
+ m_params.filter_policy = BLE_HCI_ADV_FILT_NONE;
+ return;
+ }
+ if (scanRequestWhitelistOnly && !connectWhitelistOnly) {
+ m_params.filter_policy = BLE_HCI_ADV_FILT_SCAN;
+ return;
+ }
+ if (!scanRequestWhitelistOnly && connectWhitelistOnly) {
+ m_params.filter_policy = BLE_HCI_ADV_FILT_CONN;
+ return;
+ }
+ if (scanRequestWhitelistOnly && connectWhitelistOnly) {
+ m_params.filter_policy = BLE_HCI_ADV_FILT_BOTH;
+ return;
+ }
+} // setScanFilter
+
+
+/**
+ * @brief Sets the peer to directly advertise to.
+ * @param [in] addr The address of the peer to direct the advertisements.
+ */
+void NimBLEExtAdvertisement::setDirectedPeer(const NimBLEAddress & addr) {
+ ble_addr_t peerAddr;
+ memcpy(&peerAddr.val, addr.getNative(), 6);
+ peerAddr.type = addr.getType();
+ m_params.peer = peerAddr;
+} // setDirectedPeer
+
+
+/**
+ * @brief Enable or disable direct advertisements to the peer set with `NimBLEExtAdvertisement::setDirectedPeer`
+ * @param [in] val true = send directed advertisements to peer.
+ * @param [in] high_duty true = use fast advertising rate, default - true.
+ */
+void NimBLEExtAdvertisement::setDirected(bool val, bool high_duty) {
+ m_params.directed = val;
+ m_params.high_duty_directed = high_duty;
+} // setDirected
+
+
+/**
+ * @brief Set the minimum advertising interval.
+ * @param [in] mininterval Minimum value for advertising interval in 0.625ms units, 0 = use default.
+ */
+void NimBLEExtAdvertisement::setMinInterval(uint32_t mininterval) {
+ m_params.itvl_min = mininterval;
+} // setMinInterval
+
+
+/**
+ * @brief Set the maximum advertising interval.
+ * @param [in] maxinterval Maximum value for advertising interval in 0.625ms units, 0 = use default.
+ */
+void NimBLEExtAdvertisement::setMaxInterval(uint32_t maxinterval) {
+ m_params.itvl_max = maxinterval;
+} // setMaxInterval
+
+
+/**
+ * @brief Set the primary advertising PHY to use
+ * @param [in] phy Can be one of following constants:
+ * * BLE_HCI_LE_PHY_1M
+ * * BLE_HCI_LE_PHY_CODED
+ */
+void NimBLEExtAdvertisement::setPrimaryPhy(uint8_t phy) {
+ m_params.primary_phy = phy;
+} // setPrimaryPhy
+
+
+/**
+ * @brief Set the secondary advertising PHY to use
+ * @param [in] phy Can be one of following constants:
+ * * BLE_HCI_LE_PHY_1M
+ * * BLE_HCI_LE_PHY_2M
+ * * BLE_HCI_LE_PHY_CODED
+ */
+void NimBLEExtAdvertisement::setSecondaryPhy(uint8_t phy) {
+ m_params.secondary_phy = phy;
+} // setSecondaryPhy
+
+
+/**
+ * @brief Sets whether the advertisement should be anonymous.
+ * @param [in] val Set to true to enable anonymous advertising.
+ *
+ * @details Anonymous advertising omits the device's address from the advertisement.
+ */
+void NimBLEExtAdvertisement::setAnonymous(bool val) {
+ m_params.anonymous = val;
+} // setAnonymous
+
+
+/**
+ * @brief Sets whether the scan response request callback should be called.
+ * @param [in] enable If true the scan response request callback will be called for this advertisement.
+ */
+void NimBLEExtAdvertisement::enableScanRequestCallback(bool enable) {
+ m_params.scan_req_notif = enable;
+} // enableScanRequestCallback
+
+
+/**
+ * @brief Clears the data stored in this instance, does not change settings.
+ * @details This will clear all data but preserves advertising parameter settings.
+ */
+void NimBLEExtAdvertisement::clearData() {
+ std::vector swap;
+ std::swap(m_payload, swap);
+}
+
+
+/**
+ * @brief Get the size of the current data.
+ */
+size_t NimBLEExtAdvertisement::getDataSize() {
+ return m_payload.size();
+} // getDataSize
+
+
+/**
+ * @brief Set the advertisement data.
+ * @param [in] data The data to be set as the payload.
+ * @param [in] length The size of data.
+ * @details This will completely replace any data that was previously set.
+ */
+void NimBLEExtAdvertisement::setData(const uint8_t * data, size_t length) {
+ m_payload.assign(data, data + length);
+} // setData
+
+
+/**
+ * @brief Add data to the payload to be advertised.
+ * @param [in] data The data to be added to the payload.
+ */
+void NimBLEExtAdvertisement::addData(const std::string &data) {
+ addData((uint8_t*)data.data(), data.length());
+} // addData
+
+
+/**
+ * @brief Add data to the payload to be advertised.
+ * @param [in] data The data to be added to the payload.
+ * @param [in] length The size of data to be added to the payload.
+ */
+void NimBLEExtAdvertisement::addData(const uint8_t * data, size_t length) {
+ m_payload.insert(m_payload.end(), data, data + length);
+} // addData
+
+
+/**
+ * @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];
+ cdata[0] = 3;
+ cdata[1] = BLE_HS_ADV_TYPE_APPEARANCE; // 0x19
+ addData(std::string(cdata, 2) + std::string((char*) &appearance, 2));
+} // setAppearance
+
+
+/**
+ * @brief Set the advertisement flags.
+ * @param [in] flag The flags to be set in the advertisement.
+ * * BLE_HS_ADV_F_DISC_LTD
+ * * BLE_HS_ADV_F_DISC_GEN
+ * * BLE_HS_ADV_F_BREDR_UNSUP - must always use with NimBLE
+ */
+void NimBLEExtAdvertisement::setFlags(uint8_t flag) {
+ char cdata[3];
+ cdata[0] = 2;
+ cdata[1] = BLE_HS_ADV_TYPE_FLAGS; // 0x01
+ cdata[2] = flag | BLE_HS_ADV_F_BREDR_UNSUP;
+ addData(std::string(cdata, 3));
+} // setFlags
+
+
+/**
+ * @brief Set manufacturer specific data.
+ * @param [in] data The manufacturer data to advertise.
+ */
+void NimBLEExtAdvertisement::setManufacturerData(const std::string &data) {
+ char cdata[2];
+ cdata[0] = data.length() + 1;
+ cdata[1] = BLE_HS_ADV_TYPE_MFG_DATA ; // 0xff
+ addData(std::string(cdata, 2) + data);
+} // setManufacturerData
+
+
+/**
+ * @brief Set the URI to advertise.
+ * @param [in] uri The uri to advertise.
+ */
+void NimBLEExtAdvertisement::setURI(const std::string &uri) {
+ char cdata[2];
+ cdata[0] = uri.length() + 1;
+ cdata[1] = BLE_HS_ADV_TYPE_URI;
+ addData(std::string(cdata, 2) + uri);
+} // setURI
+
+
+/**
+ * @brief Set the complete name of this device.
+ * @param [in] name The name to advertise.
+ */
+void NimBLEExtAdvertisement::setName(const std::string &name) {
+ char cdata[2];
+ cdata[0] = name.length() + 1;
+ cdata[1] = BLE_HS_ADV_TYPE_COMP_NAME; // 0x09
+ addData(std::string(cdata, 2) + name);
+} // setName
+
+
+/**
+ * @brief Set a single service to advertise as a complete list of services.
+ * @param [in] uuid The service to advertise.
+ */
+void NimBLEExtAdvertisement::setCompleteServices(const NimBLEUUID &uuid) {
+ setServices(true, uuid.bitSize(), {uuid});
+} // setCompleteServices
+
+
+/**
+ * @brief Set the complete list of 16 bit services to advertise.
+ * @param [in] v_uuid A vector of 16 bit UUID's to advertise.
+ */
+void NimBLEExtAdvertisement::setCompleteServices16(const std::vector& v_uuid) {
+ setServices(true, 16, v_uuid);
+} // setCompleteServices16
+
+
+/**
+ * @brief Set the complete list of 32 bit services to advertise.
+ * @param [in] v_uuid A vector of 32 bit UUID's to advertise.
+ */
+void NimBLEExtAdvertisement::setCompleteServices32(const std::vector& v_uuid) {
+ setServices(true, 32, v_uuid);
+} // setCompleteServices32
+
+
+/**
+ * @brief Set a single service to advertise as a partial list of services.
+ * @param [in] uuid The service to advertise.
+ */
+void NimBLEExtAdvertisement::setPartialServices(const NimBLEUUID &uuid) {
+ setServices(false, uuid.bitSize(), {uuid});
+} // setPartialServices
+
+
+/**
+ * @brief Set the partial list of services to advertise.
+ * @param [in] v_uuid A vector of 16 bit UUID's to advertise.
+ */
+void NimBLEExtAdvertisement::setPartialServices16(const std::vector& v_uuid) {
+ setServices(false, 16, v_uuid);
+} // setPartialServices16
+
+
+/**
+ * @brief Set the partial list of services to advertise.
+ * @param [in] v_uuid A vector of 32 bit UUID's to advertise.
+ */
+void NimBLEExtAdvertisement::setPartialServices32(const std::vector& v_uuid) {
+ setServices(false, 32, v_uuid);
+} // setPartialServices32
+
+
+/**
+ * @brief Utility function to create the list of service UUID's from a vector.
+ * @param [in] complete If true the vector is the complete set of services.
+ * @param [in] size The bit size of the UUID's in the vector. (16, 32, or 128).
+ * @param [in] v_uuid The vector of service UUID's to advertise.
+ */
+void NimBLEExtAdvertisement::setServices(const bool complete, const uint8_t size,
+ const std::vector &v_uuid)
+{
+ char cdata[2];
+ cdata[0] = (size / 8) * v_uuid.size() + 1;
+ switch(size) {
+ case 16:
+ cdata[1] = complete ? BLE_HS_ADV_TYPE_COMP_UUIDS16 : BLE_HS_ADV_TYPE_INCOMP_UUIDS16;
+ break;
+ case 32:
+ cdata[1] = complete ? BLE_HS_ADV_TYPE_COMP_UUIDS32 : BLE_HS_ADV_TYPE_INCOMP_UUIDS32;
+ break;
+ case 128:
+ cdata[1] = complete ? BLE_HS_ADV_TYPE_COMP_UUIDS128 : BLE_HS_ADV_TYPE_INCOMP_UUIDS128;
+ break;
+ default:
+ return;
+ }
+
+ std::string uuids;
+
+ for(auto &it : v_uuid){
+ if(it.bitSize() != size) {
+ NIMBLE_LOGE(LOG_TAG, "Service UUID(%d) invalid", size);
+ return;
+ } else {
+ switch(size) {
+ case 16:
+ uuids += std::string((char*)&it.getNative()->u16.value, 2);
+ break;
+ case 32:
+ uuids += std::string((char*)&it.getNative()->u32.value, 4);
+ break;
+ case 128:
+ uuids += std::string((char*)&it.getNative()->u128.value, 16);
+ break;
+ default:
+ return;
+ }
+ }
+ }
+
+ addData(std::string(cdata, 2) + uuids);
+} // setServices
+
+
+/**
+ * @brief Set the service data (UUID + data)
+ * @param [in] uuid The UUID to set with the service data.
+ * @param [in] data The data to be associated with the service data advertised.
+ */
+void NimBLEExtAdvertisement::setServiceData(const NimBLEUUID &uuid, const std::string &data) {
+ char cdata[2];
+ switch (uuid.bitSize()) {
+ case 16: {
+ // [Len] [0x16] [UUID16] data
+ cdata[0] = data.length() + 3;
+ cdata[1] = BLE_HS_ADV_TYPE_SVC_DATA_UUID16; // 0x16
+ addData(std::string(cdata, 2) + std::string((char*) &uuid.getNative()->u16.value, 2) + data);
+ break;
+ }
+
+ case 32: {
+ // [Len] [0x20] [UUID32] data
+ cdata[0] = data.length() + 5;
+ cdata[1] = BLE_HS_ADV_TYPE_SVC_DATA_UUID32; // 0x20
+ addData(std::string(cdata, 2) + std::string((char*) &uuid.getNative()->u32.value, 4) + data);
+ break;
+ }
+
+ case 128: {
+ // [Len] [0x21] [UUID128] data
+ cdata[0] = data.length() + 17;
+ cdata[1] = BLE_HS_ADV_TYPE_SVC_DATA_UUID128; // 0x21
+ addData(std::string(cdata, 2) + std::string((char*) &uuid.getNative()->u128.value, 16) + data);
+ break;
+ }
+
+ default:
+ return;
+ }
+} // setServiceData
+
+
+/**
+ * @brief Set the short name.
+ * @param [in] name The short name of the device.
+ */
+void NimBLEExtAdvertisement::setShortName(const std::string &name) {
+ char cdata[2];
+ cdata[0] = name.length() + 1;
+ cdata[1] = BLE_HS_ADV_TYPE_INCOMP_NAME; // 0x08
+ addData(std::string(cdata, 2) + name);
+} // setShortName
+
+
+/**
+ * @brief Adds Tx power level to the advertisement data.
+ */
+void NimBLEExtAdvertisement::addTxPower() {
+ m_params.include_tx_power = 1;
+} // addTxPower
+
+
+/**
+ * @brief Set the preferred connection interval parameters.
+ * @param [in] min The minimum interval desired.
+ * @param [in] max The maximum interval desired.
+ */
+void NimBLEExtAdvertisement::setPreferredParams(uint16_t min, uint16_t max) {
+ uint8_t data[6];
+ data[0] = BLE_HS_ADV_SLAVE_ITVL_RANGE_LEN + 1;
+ data[1] = BLE_HS_ADV_TYPE_SLAVE_ITVL_RANGE;
+ data[2] = min;
+ data[3] = min >> 8;
+ data[4] = max;
+ data[5] = max >> 8;
+ addData(data, 6);
+} // setPreferredParams
+
+#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_BROADCASTER && CONFIG_BT_NIMBLE_EXT_ADV */
diff --git a/src/NimBLEExtAdvertising.h b/src/NimBLEExtAdvertising.h
new file mode 100644
index 0000000..b1f21fc
--- /dev/null
+++ b/src/NimBLEExtAdvertising.h
@@ -0,0 +1,152 @@
+/*
+ * NimBLEExtAdvertising.h
+ *
+ * Created: on February 6, 2022
+ * Author H2zero
+ */
+
+#ifndef MAIN_BLEEXTADVERTISING_H_
+#define MAIN_BLEEXTADVERTISING_H_
+#include "nimconfig.h"
+#if defined(CONFIG_BT_ENABLED) && \
+ defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER) && \
+ CONFIG_BT_NIMBLE_EXT_ADV
+
+# if defined(CONFIG_NIMBLE_CPP_IDF)
+# include "host/ble_gap.h"
+# else
+# include "nimble/nimble/host/include/host/ble_gap.h"
+# endif
+
+/**** FIX COMPILATION ****/
+#undef min
+#undef max
+/**************************/
+
+#include "NimBLEAddress.h"
+#include "NimBLEUUID.h"
+
+#include
+
+class NimBLEExtAdvertisingCallbacks;
+
+
+/**
+ * @brief Extended advertisement data
+ */
+class NimBLEExtAdvertisement {
+public:
+ NimBLEExtAdvertisement(uint8_t priPhy = BLE_HCI_LE_PHY_1M,
+ uint8_t secPhy = BLE_HCI_LE_PHY_1M);
+ void setAppearance(uint16_t appearance);
+ void setCompleteServices(const NimBLEUUID &uuid);
+ void setCompleteServices16(const std::vector &v_uuid);
+ void setCompleteServices32(const std::vector &v_uuid);
+ void setFlags(uint8_t flag);
+ void setManufacturerData(const std::string &data);
+ void setURI(const std::string &uri);
+ void setName(const std::string &name);
+ void setPartialServices(const NimBLEUUID &uuid);
+ void setPartialServices16(const std::vector &v_uuid);
+ void setPartialServices32(const std::vector &v_uuid);
+ void setServiceData(const NimBLEUUID &uuid, const std::string &data);
+ void setShortName(const std::string &name);
+ void setData(const uint8_t * data, size_t length);
+ void addData(const std::string &data);
+ void addData(const uint8_t * data, size_t length);
+ void addTxPower();
+ void setPreferredParams(uint16_t min, uint16_t max);
+ void setLegacyAdvertising(bool val);
+ void setConnectable(bool val);
+ void setScannable(bool val);
+ void setMinInterval(uint32_t mininterval);
+ void setMaxInterval(uint32_t maxinterval);
+ void setPrimaryPhy(uint8_t phy);
+ void setSecondaryPhy(uint8_t phy);
+ void setScanFilter(bool scanRequestWhitelistOnly, bool connectWhitelistOnly);
+ void setDirectedPeer(const NimBLEAddress & addr);
+ void setDirected(bool val, bool high_duty = true);
+ void setAnonymous(bool val);
+ void setPrimaryChannels(bool ch37, bool ch38, bool ch39);
+ void setTxPower(int8_t dbm);
+ void setAddress(const NimBLEAddress & addr);
+ void enableScanRequestCallback(bool enable);
+ void clearData();
+ size_t getDataSize();
+
+private:
+ friend class NimBLEExtAdvertising;
+
+ void setServices(const bool complete, const uint8_t size,
+ const std::vector &v_uuid);
+
+ std::vector m_payload;
+ ble_gap_ext_adv_params m_params;
+ NimBLEAddress m_advAddress;
+}; // NimBLEExtAdvertisement
+
+
+/**
+ * @brief Extended advertising class.
+ */
+class NimBLEExtAdvertising {
+public:
+ /**
+ * @brief Construct an extended advertising object.
+ */
+ NimBLEExtAdvertising() :m_advStatus(CONFIG_BT_NIMBLE_MAX_EXT_ADV_INSTANCES + 1, false) {}
+ ~NimBLEExtAdvertising();
+ bool start(uint8_t inst_id, int duration = 0, int max_events = 0);
+ bool setInstanceData(uint8_t inst_id, NimBLEExtAdvertisement& adv);
+ bool setScanResponseData(uint8_t inst_id, NimBLEExtAdvertisement & data);
+ bool removeInstance(uint8_t inst_id);
+ bool removeAll();
+ bool stop(uint8_t inst_id);
+ bool stop();
+ bool isActive(uint8_t inst_id);
+ bool isAdvertising();
+ void setCallbacks(NimBLEExtAdvertisingCallbacks* callbacks,
+ bool deleteCallbacks = true);
+
+private:
+ friend class NimBLEDevice;
+ friend class NimBLEServer;
+
+ void onHostSync();
+ static int handleGapEvent(struct ble_gap_event *event, void *arg);
+
+ bool m_scanResp;
+ bool m_deleteCallbacks;
+ NimBLEExtAdvertisingCallbacks* m_pCallbacks;
+ ble_gap_ext_adv_params m_advParams;
+ std::vector m_advStatus;
+};
+
+
+/**
+ * @brief Callbacks associated with NimBLEExtAdvertising class.
+ */
+class NimBLEExtAdvertisingCallbacks {
+public:
+ virtual ~NimBLEExtAdvertisingCallbacks() {};
+
+ /**
+ * @brief Handle an advertising stop event.
+ * @param [in] pAdv A convenience pointer to the extended advertising interface.
+ * @param [in] reason The reason code for stopping the advertising.
+ * @param [in] inst_id The instance ID of the advertisement that was stopped.
+ */
+ virtual void onStopped(NimBLEExtAdvertising *pAdv, int reason, uint8_t inst_id);
+
+ /**
+ * @brief Handle a scan response request.
+ * This is called when a scanning device requests a scan response.
+ * @param [in] pAdv A convenience pointer to the extended advertising interface.
+ * @param [in] inst_id The instance ID of the advertisement that the scan response request was made.
+ * @param [in] addr The address of the device making the request.
+ */
+ virtual void onScanRequest(NimBLEExtAdvertising *pAdv, uint8_t inst_id, NimBLEAddress addr);
+}; // NimBLEExtAdvertisingCallbacks
+
+#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_BROADCASTER && CONFIG_BT_NIMBLE_EXT_ADV */
+#endif /* MAIN_BLEADVERTISING_H_ */
diff --git a/src/NimBLEScan.cpp b/src/NimBLEScan.cpp
index a19db9d..eb87df8 100644
--- a/src/NimBLEScan.cpp
+++ b/src/NimBLEScan.cpp
@@ -56,8 +56,8 @@ NimBLEScan::~NimBLEScan() {
* @param [in] param Parameter data for this event.
*/
/*STATIC*/int NimBLEScan::handleGapEvent(ble_gap_event* event, void* arg) {
-
- NimBLEScan* pScan = (NimBLEScan*)arg;
+ (void)arg;
+ NimBLEScan* pScan = NimBLEDevice::getScan();
switch(event->type) {
@@ -67,16 +67,16 @@ NimBLEScan::~NimBLEScan() {
NIMBLE_LOGI(LOG_TAG, "Scan op in progress - ignoring results");
return 0;
}
-#if MYNEWT_VAL(BLE_EXT_ADV)
+#if CONFIG_BT_NIMBLE_EXT_ADV
const auto& disc = event->ext_disc;
- const auto event_type = disc.legacy_event_type;
+ const bool isLegacyAdv = disc.props & BLE_HCI_ADV_LEGACY_MASK;
+ const auto event_type = isLegacyAdv ? disc.legacy_event_type : disc.props;
#else
const auto& disc = event->disc;
+ const bool isLegacyAdv = true;
const auto event_type = disc.event_type;
#endif
-
NimBLEAddress advertisedAddress(disc.addr);
-
// Examine our list of ignored addresses and stop processing if we don't want to see it or are already connected
if(NimBLEDevice::isIgnored(advertisedAddress)) {
@@ -88,7 +88,12 @@ NimBLEScan::~NimBLEScan() {
// If we've seen this device before get a pointer to it from the vector
for(auto &it: pScan->m_scanResults.m_advertisedDevicesVector) {
- if(it->getAddress() == advertisedAddress) {
+#if CONFIG_BT_NIMBLE_EXT_ADV
+ // Same address but different set ID should create a new advertised device.
+ if (it->getAddress() == advertisedAddress && it->getSetId() == disc.sid) {
+#else
+ if (it->getAddress() == advertisedAddress) {
+#endif
advertisedDevice = it;
break;
}
@@ -96,20 +101,27 @@ NimBLEScan::~NimBLEScan() {
// If we haven't seen this device before; create a new instance and insert it in the vector.
// Otherwise just update the relevant parameters of the already known device.
- if(advertisedDevice == nullptr && event_type != BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP){
+ if (advertisedDevice == nullptr &&
+ (!isLegacyAdv || event_type != BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP)) {
// Check if we have reach the scan results limit, ignore this one if so.
// We still need to store each device when maxResults is 0 to be able to append the scan results
- if(pScan->m_maxResults > 0 && pScan->m_maxResults < 0xFF &&
- (pScan->m_scanResults.m_advertisedDevicesVector.size() >= pScan->m_maxResults))
- {
+ if (pScan->m_maxResults > 0 && pScan->m_maxResults < 0xFF &&
+ (pScan->m_scanResults.m_advertisedDevicesVector.size() >= pScan->m_maxResults)) {
return 0;
}
+
advertisedDevice = new NimBLEAdvertisedDevice();
advertisedDevice->setAddress(advertisedAddress);
- advertisedDevice->setAdvType(event_type);
+ advertisedDevice->setAdvType(event_type, isLegacyAdv);
+#if CONFIG_BT_NIMBLE_EXT_ADV
+ advertisedDevice->setSetId(disc.sid);
+ advertisedDevice->setPrimaryPhy(disc.prim_phy);
+ advertisedDevice->setSecondaryPhy(disc.sec_phy);
+ advertisedDevice->setPeriodicInterval(disc.periodic_adv_itvl);
+#endif
pScan->m_scanResults.m_advertisedDevicesVector.push_back(advertisedDevice);
NIMBLE_LOGI(LOG_TAG, "New advertiser: %s", advertisedAddress.toString().c_str());
- } else if(advertisedDevice != nullptr) {
+ } else if (advertisedDevice != nullptr) {
NIMBLE_LOGI(LOG_TAG, "Updated advertiser: %s", advertisedAddress.toString().c_str());
} else {
// Scan response from unknown device
@@ -118,13 +130,13 @@ NimBLEScan::~NimBLEScan() {
advertisedDevice->m_timestamp = time(nullptr);
advertisedDevice->setRSSI(disc.rssi);
- advertisedDevice->setPayload(disc.data, disc.length_data,
- event_type == BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP);
+ advertisedDevice->setPayload(disc.data, disc.length_data, (isLegacyAdv &&
+ event_type == BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP));
if (pScan->m_pAdvertisedDeviceCallbacks) {
// If not active scanning or scan response is not available
- // report the result to the callback now.
- if(pScan->m_scan_params.passive ||
+ // or extended advertisement scanning, report the result to the callback now.
+ if(pScan->m_scan_params.passive || !isLegacyAdv ||
(advertisedDevice->getAdvType() != BLE_HCI_ADV_TYPE_ADV_IND &&
advertisedDevice->getAdvType() != BLE_HCI_ADV_TYPE_ADV_SCAN_IND))
{
@@ -132,7 +144,7 @@ NimBLEScan::~NimBLEScan() {
pScan->m_pAdvertisedDeviceCallbacks->onResult(advertisedDevice);
// Otherwise, wait for the scan response so we can report the complete data.
- } else if (event_type == BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP) {
+ } else if (isLegacyAdv && event_type == BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP) {
advertisedDevice->m_callbackSent = true;
pScan->m_pAdvertisedDeviceCallbacks->onResult(advertisedDevice);
}
@@ -313,17 +325,27 @@ bool NimBLEScan::start(uint32_t duration, void (*scanCompleteCB)(NimBLEScanResul
m_ignoreResults = true;
}
-
-#if MYNEWT_VAL(BLE_EXT_ADV)
- ble_gap_ext_disc_params scan_params;
- scan_params.passive = m_scan_params.passive;
- scan_params.itvl = m_scan_params.itvl;
- scan_params.window = m_scan_params.window;
- int rc = ble_gap_ext_disc(NimBLEDevice::m_own_addr_type, duration/10, 0, m_scan_params.filter_duplicates, m_scan_params.filter_policy, m_scan_params.limited, &scan_params, &scan_params,
- NimBLEScan::handleGapEvent, this);
+# if CONFIG_BT_NIMBLE_EXT_ADV
+ ble_gap_ext_disc_params scan_params;
+ scan_params.passive = m_scan_params.passive;
+ scan_params.itvl = m_scan_params.itvl;
+ scan_params.window = m_scan_params.window;
+ int rc = ble_gap_ext_disc(NimBLEDevice::m_own_addr_type,
+ duration/10,
+ 0,
+ m_scan_params.filter_duplicates,
+ m_scan_params.filter_policy,
+ m_scan_params.limited,
+ &scan_params,
+ &scan_params,
+ NimBLEScan::handleGapEvent,
+ NULL);
#else
- int rc = ble_gap_disc(NimBLEDevice::m_own_addr_type, duration, &m_scan_params,
- NimBLEScan::handleGapEvent, this);
+ int rc = ble_gap_disc(NimBLEDevice::m_own_addr_type,
+ duration,
+ &m_scan_params,
+ NimBLEScan::handleGapEvent,
+ NULL);
#endif
switch(rc) {
case 0:
diff --git a/src/NimBLEServer.cpp b/src/NimBLEServer.cpp
index 03ee785..f75dd62 100644
--- a/src/NimBLEServer.cpp
+++ b/src/NimBLEServer.cpp
@@ -42,7 +42,9 @@ NimBLEServer::NimBLEServer() {
// m_svcChgChrHdl = 0xffff; // Future Use
m_pServerCallbacks = &defaultCallbacks;
m_gattsStarted = false;
+#if !CONFIG_BT_NIMBLE_EXT_ADV
m_advertiseOnDisconnect = true;
+#endif
m_svcChanged = false;
m_deleteCallbacks = true;
} // NimBLEServer
@@ -139,15 +141,26 @@ NimBLEService *NimBLEServer::getServiceByHandle(uint16_t handle) {
return nullptr;
}
+
+#if CONFIG_BT_NIMBLE_EXT_ADV
+/**
+ * @brief Retrieve the advertising object that can be used to advertise the existence of the server.
+ * @return An advertising object.
+ */
+NimBLEExtAdvertising* NimBLEServer::getAdvertising() {
+ return NimBLEDevice::getAdvertising();
+} // getAdvertising
+#endif
+
+#if !CONFIG_BT_NIMBLE_EXT_ADV || defined(_DOXYGEN_)
/**
* @brief Retrieve the advertising object that can be used to advertise the existence of the server.
- *
* @return An advertising object.
*/
NimBLEAdvertising* NimBLEServer::getAdvertising() {
return NimBLEDevice::getAdvertising();
} // getAdvertising
-
+#endif
/**
* @brief Sends a service changed notification and resets the GATT server.
@@ -240,6 +253,7 @@ int NimBLEServer::disconnect(uint16_t connId, uint8_t reason) {
} // disconnect
+#if !CONFIG_BT_NIMBLE_EXT_ADV || defined(_DOXYGEN_)
/**
* @brief Set the server to automatically start advertising when a client disconnects.
* @param [in] aod true == advertise, false == don't advertise.
@@ -247,7 +261,7 @@ int NimBLEServer::disconnect(uint16_t connId, uint8_t reason) {
void NimBLEServer::advertiseOnDisconnect(bool aod) {
m_advertiseOnDisconnect = aod;
} // advertiseOnDisconnect
-
+#endif
/**
* @brief Return the number of connected clients.
@@ -325,7 +339,7 @@ NimBLEConnInfo NimBLEServer::getPeerIDInfo(uint16_t id) {
*/
/*STATIC*/
int NimBLEServer::handleGapEvent(struct ble_gap_event *event, void *arg) {
- NimBLEServer* server = (NimBLEServer*)arg;
+ NimBLEServer* server = NimBLEDevice::getServer();
NIMBLE_LOGD(LOG_TAG, ">> handleGapEvent: %s",
NimBLEUtils::gapEventToString(event->type));
int rc = 0;
@@ -337,7 +351,9 @@ int NimBLEServer::handleGapEvent(struct ble_gap_event *event, void *arg) {
if (event->connect.status != 0) {
/* Connection failed; resume advertising */
NIMBLE_LOGE(LOG_TAG, "Connection failed");
+#if !CONFIG_BT_NIMBLE_EXT_ADV
NimBLEDevice::startAdvertising();
+#endif
}
else {
server->m_connectedPeersVec.push_back(event->connect.conn_handle);
@@ -382,9 +398,11 @@ int NimBLEServer::handleGapEvent(struct ble_gap_event *event, void *arg) {
server->m_pServerCallbacks->onDisconnect(server);
server->m_pServerCallbacks->onDisconnect(server, &event->disconnect.conn);
+#if !CONFIG_BT_NIMBLE_EXT_ADV
if(server->m_advertiseOnDisconnect) {
server->startAdvertising();
}
+#endif
return 0;
} // BLE_GAP_EVENT_DISCONNECT
@@ -472,11 +490,15 @@ int NimBLEServer::handleGapEvent(struct ble_gap_event *event, void *arg) {
return 0;
} // BLE_GAP_EVENT_NOTIFY_TX
- case BLE_GAP_EVENT_ADV_COMPLETE: {
- NIMBLE_LOGD(LOG_TAG, "Advertising Complete");
- NimBLEDevice::getAdvertising()->advCompleteCB();
- return 0;
- }
+
+ case BLE_GAP_EVENT_ADV_COMPLETE:
+#if CONFIG_BT_NIMBLE_EXT_ADV
+ case BLE_GAP_EVENT_SCAN_REQ_RCVD:
+ return NimBLEExtAdvertising::handleGapEvent(event, arg);
+#else
+ return NimBLEAdvertising::handleGapEvent(event, arg);
+#endif
+ // BLE_GAP_EVENT_ADV_COMPLETE | BLE_GAP_EVENT_SCAN_REQ_RCVD
case BLE_GAP_EVENT_CONN_UPDATE: {
NIMBLE_LOGD(LOG_TAG, "Connection parameters updated.");
@@ -653,7 +675,9 @@ void NimBLEServer::removeService(NimBLEService* service, bool deleteSvc) {
service->m_removed = deleteSvc ? NIMBLE_ATT_REMOVE_DELETE : NIMBLE_ATT_REMOVE_HIDE;
serviceChanged();
+#if !CONFIG_BT_NIMBLE_EXT_ADV
NimBLEDevice::getAdvertising()->removeServiceUUID(service->getUUID());
+#endif
}
@@ -716,22 +740,52 @@ void NimBLEServer::resetGATT() {
}
+#if CONFIG_BT_NIMBLE_EXT_ADV
/**
* @brief Start advertising.
- *
- * Start the server advertising its existence. This is a convenience function and is equivalent to
+ * @param [in] inst_id The extended advertisement instance ID to start.
+ * @param [in] duration How long to advertise for in milliseconds, 0 = forever (default).
+ * @param [in] max_events Maximum number of advertisement events to send, 0 = no limit (default).
+ * @return True if advertising started successfully.
+ * @details Start the server advertising its existence. This is a convenience function and is equivalent to
* retrieving the advertising object and invoking start upon it.
*/
-void NimBLEServer::startAdvertising() {
- NimBLEDevice::startAdvertising();
+bool NimBLEServer::startAdvertising(uint8_t inst_id,
+ int duration,
+ int max_events) {
+ return getAdvertising()->start(inst_id, duration, max_events);
} // startAdvertising
/**
- * @brief Stop advertising.
+ * @brief Convenience function to stop advertising a data set.
+ * @param [in] inst_id The extended advertisement instance ID to stop advertising.
+ * @return True if advertising stopped successfully.
*/
-void NimBLEServer::stopAdvertising() {
- NimBLEDevice::stopAdvertising();
+bool NimBLEServer::stopAdvertising(uint8_t inst_id) {
+ return getAdvertising()->stop(inst_id);
+} // stopAdvertising
+#endif
+
+#if !CONFIG_BT_NIMBLE_EXT_ADV|| defined(_DOXYGEN_)
+/**
+ * @brief Start advertising.
+ * @return True if advertising started successfully.
+ * @details Start the server advertising its existence. This is a convenience function and is equivalent to
+ * retrieving the advertising object and invoking start upon it.
+ */
+bool NimBLEServer::startAdvertising() {
+ return getAdvertising()->start();
+} // startAdvertising
+#endif
+
+
+/**
+ * @brief Stop advertising.
+ * @return True if advertising stopped successfully.
+ */
+bool NimBLEServer::stopAdvertising() {
+ return getAdvertising()->stop();
} // stopAdvertising
diff --git a/src/NimBLEServer.h b/src/NimBLEServer.h
index 605445a..5b946f0 100644
--- a/src/NimBLEServer.h
+++ b/src/NimBLEServer.h
@@ -25,7 +25,11 @@
#include "NimBLEUtils.h"
#include "NimBLEAddress.h"
+#if CONFIG_BT_NIMBLE_EXT_ADV
+#include "NimBLEExtAdvertising.h"
+#else
#include "NimBLEAdvertising.h"
+#endif
#include "NimBLEService.h"
#include "NimBLESecurity.h"
#include "NimBLEConnInfo.h"
@@ -46,11 +50,19 @@ public:
NimBLEService* createService(const NimBLEUUID &uuid);
void removeService(NimBLEService* service, bool deleteSvc = false);
void addService(NimBLEService* service);
- NimBLEAdvertising* getAdvertising();
void setCallbacks(NimBLEServerCallbacks* pCallbacks,
bool deleteCallbacks = true);
- void startAdvertising();
- void stopAdvertising();
+#if CONFIG_BT_NIMBLE_EXT_ADV
+ NimBLEExtAdvertising* getAdvertising();
+ bool startAdvertising(uint8_t inst_id,
+ int duration = 0,
+ int max_events = 0);
+ bool stopAdvertising(uint8_t inst_id);
+#else
+ NimBLEAdvertising* getAdvertising();
+ bool startAdvertising();
+#endif
+ bool stopAdvertising();
void start();
NimBLEService* getServiceByUUID(const char* uuid, uint16_t instanceId = 0);
NimBLEService* getServiceByUUID(const NimBLEUUID &uuid, uint16_t instanceId = 0);
@@ -66,7 +78,9 @@ public:
NimBLEConnInfo getPeerInfo(size_t index);
NimBLEConnInfo getPeerInfo(const NimBLEAddress& address);
NimBLEConnInfo getPeerIDInfo(uint16_t id);
+#if !CONFIG_BT_NIMBLE_EXT_ADV || defined(_DOXYGEN_)
void advertiseOnDisconnect(bool);
+#endif
private:
NimBLEServer();
@@ -75,9 +89,15 @@ private:
friend class NimBLEService;
friend class NimBLEDevice;
friend class NimBLEAdvertising;
+#if CONFIG_BT_NIMBLE_EXT_ADV
+ friend class NimBLEExtAdvertising;
+ friend class NimBLEExtAdvertisementData;
+#endif
bool m_gattsStarted;
+#if !CONFIG_BT_NIMBLE_EXT_ADV
bool m_advertiseOnDisconnect;
+#endif
bool m_svcChanged;
NimBLEServerCallbacks* m_pServerCallbacks;
bool m_deleteCallbacks;