Update examples

This commit is contained in:
h2zero 2024-12-09 12:58:14 -07:00 committed by h2zero
parent e4d2e991f1
commit 49769f4aeb
66 changed files with 629 additions and 1705 deletions

View file

@ -1,7 +1,7 @@
name: Build
on:
workflow_dispatch: # Start a workflow
workflow_dispatch:
pull_request:
push:
branches:
@ -20,8 +20,8 @@ jobs:
idf_ver: ["release-v4.4", "release-v5.1", "release-v5.3"]
idf_target: ["esp32", "esp32s3", "esp32c2", "esp32c3", "esp32c6", "esp32h2", "esp32p4"]
example:
- Advanced/NimBLE_Client
- Advanced/NimBLE_Server
- NimBLE_Client
- NimBLE_Server
- Bluetooth_5/NimBLE_extended_client
- Bluetooth_5/NimBLE_extended_server
exclude:
@ -29,8 +29,6 @@ jobs:
example: Bluetooth_5/NimBLE_extended_client
- idf_target: "esp32"
example: Bluetooth_5/NimBLE_extended_server
- idf_target: "esp32"
example: Bluetooth_5/NimBLE_multi_advertiser
- idf_ver: release-v4.4
idf_target: "esp32c2"
- idf_ver: release-v4.4

View file

@ -1,3 +0,0 @@
PROJECT_NAME := NimBLE_Client
include $(IDF_PATH)/make/project.mk

View file

@ -1,4 +0,0 @@
#
# "main" pseudo-component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)

View file

@ -1,369 +0,0 @@
/** NimBLE_Client Demo:
*
* Demonstrates many of the available features of the NimBLE client library.
*
* Created: on March 24 2020
* Author: H2zero
*
*/
#include <NimBLEDevice.h>
extern "C" {void app_main(void);}
static const NimBLEAdvertisedDevice* advDevice;
static bool doConnect = false;
static uint32_t scanTime = 0; /** scan time in milliseconds, 0 = scan forever */
/** None of these are required as they will be handled by the library with defaults. **
** Remove as you see fit for your needs */
class ClientCallbacks : public NimBLEClientCallbacks {
void onConnect(NimBLEClient* pClient) {
printf("Connected\n");
/** After connection we should change the parameters if we don't need fast response times.
* These settings are 150ms interval, 0 latency, 450ms timout.
* Timeout should be a multiple of the interval, minimum is 100ms.
* I find a multiple of 3-5 * the interval works best for quick response/reconnect.
* Min interval: 120 * 1.25ms = 150, Max interval: 120 * 1.25ms = 150, 0 latency, 45 * 10ms = 450ms timeout
*/
pClient->updateConnParams(120,120,0,45);
}
void onDisconnect(NimBLEClient* pClient, int reason) {
printf("%s Disconnected, reason = %d - Starting scan\n",
pClient->getPeerAddress().toString().c_str(), reason);
NimBLEDevice::getScan()->start(scanTime);
}
/********************* Security handled here **********************
****** Note: these are the same return values as defaults ********/
void onPassKeyEntry(NimBLEConnInfo& connInfo){
printf("Server Passkey Entry\n");
/** This should prompt the user to enter the passkey displayed
* on the peer device.
*/
NimBLEDevice::injectPassKey(connInfo, 123456);
};
void onConfirmPasskey(NimBLEConnInfo& connInfo, uint32_t pass_key){
printf("The passkey YES/NO number: %" PRIu32 "\n", pass_key);
/** Inject false if passkeys don't match. */
NimBLEDevice::injectConfirmPasskey(connInfo, true);
};
/** Pairing process complete, we can check the results in connInfo */
void onAuthenticationComplete(NimBLEConnInfo& connInfo){
if(!connInfo.isEncrypted()) {
printf("Encrypt connection failed - disconnecting\n");
/** Find the client with the connection handle provided in desc */
NimBLEDevice::getClientByHandle(connInfo.getConnHandle())->disconnect();
return;
}
}
};
/** Define a class to handle the callbacks when advertisments are received */
class scanCallbacks: public NimBLEScanCallbacks {
void onResult(const NimBLEAdvertisedDevice* advertisedDevice) {
printf("Advertised Device found: %s\n", advertisedDevice->toString().c_str());
if(advertisedDevice->isAdvertisingService(NimBLEUUID("DEAD")))
{
printf("Found Our Service\n");
/** stop scan before connecting */
NimBLEDevice::getScan()->stop();
/** Save the device reference in a global for the client to use*/
advDevice = advertisedDevice;
/** Ready to connect now */
doConnect = true;
}
}
/** Callback to process the results of the completed scan or restart it */
void onScanEnd(const NimBLEScanResults& results, int reason) {
printf("Scan Ended, reason: %d, device count: %d\n", reason, results.getCount());
}
};
/** Notification / Indication receiving handler callback */
void notifyCB(NimBLERemoteCharacteristic* pRemoteCharacteristic, uint8_t* pData, size_t length, bool isNotify){
std::string str = (isNotify == true) ? "Notification" : "Indication";
str += " from ";
str += pRemoteCharacteristic->getClient()->getPeerAddress().toString();
str += ": Service = " + pRemoteCharacteristic->getRemoteService()->getUUID().toString();
str += ", Characteristic = " + pRemoteCharacteristic->getUUID().toString();
str += ", Value = " + std::string((char*)pData, length);
printf("%s\n", str.c_str());
}
/** Create a single global instance of the callback class to be used by all clients */
static ClientCallbacks clientCB;
/** Handles the provisioning of clients and connects / interfaces with the server */
bool connectToServer() {
NimBLEClient* pClient = nullptr;
/** Check if we have a client we should reuse first **/
if(NimBLEDevice::getCreatedClientCount()) {
/** Special case when we already know this device, we send false as the
* second argument in connect() to prevent refreshing the service database.
* This saves considerable time and power.
*/
pClient = NimBLEDevice::getClientByPeerAddress(advDevice->getAddress());
if(pClient){
if(!pClient->connect(advDevice, false)) {
printf("Reconnect failed\n");
return false;
}
printf("Reconnected client\n");
}
/** We don't already have a client that knows this device,
* we will check for a client that is disconnected that we can use.
*/
else {
pClient = NimBLEDevice::getDisconnectedClient();
}
}
/** No client to reuse? Create a new one. */
if(!pClient) {
if(NimBLEDevice::getCreatedClientCount() >= NIMBLE_MAX_CONNECTIONS) {
printf("Max clients reached - no more connections available\n");
return false;
}
pClient = NimBLEDevice::createClient();
printf("New client created\n");
pClient->setClientCallbacks(&clientCB, false);
/** Set initial connection parameters: These settings are 15ms interval, 0 latency, 120ms timout.
* These settings are safe for 3 clients to connect reliably, can go faster if you have less
* connections. Timeout should be a multiple of the interval, minimum is 100ms.
* Min interval: 12 * 1.25ms = 15, Max interval: 12 * 1.25ms = 15, 0 latency, 12 * 10ms = 120ms timeout
*/
pClient->setConnectionParams(6,6,0,15);
/** Set how long we are willing to wait for the connection to complete (milliseconds), default is 30000. */
pClient->setConnectTimeout(5 * 1000);
if (!pClient->connect(advDevice)) {
/** 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;
}
}
if(!pClient->isConnected()) {
if (!pClient->connect(advDevice)) {
printf("Failed to connect\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;
NimBLERemoteDescriptor* pDsc = nullptr;
pSvc = pClient->getService("DEAD");
if(pSvc) { /** make sure it's not null */
pChr = pSvc->getCharacteristic("BEEF");
}
if(pChr) { /** make sure it's not null */
if(pChr->canRead()) {
printf("%s Value: %s\n",
pChr->getUUID().toString().c_str(),
pChr->readValue().c_str());
}
if(pChr->canWrite()) {
if(pChr->writeValue("Tasty")) {
printf("Wrote new value to: %s\n", pChr->getUUID().toString().c_str());
}
else {
/** Disconnect if write failed */
pClient->disconnect();
return false;
}
if(pChr->canRead()) {
printf("The value of: %s is now: %s\n",
pChr->getUUID().toString().c_str(),
pChr->readValue().c_str());
}
}
/** registerForNotify() has been removed and replaced with subscribe() / unsubscribe().
* Subscribe parameter defaults are: notifications=true, notifyCallback=nullptr, response=true.
* Unsubscribe parameter defaults are: response=true.
*/
if(pChr->canNotify()) {
//if(!pChr->registerForNotify(notifyCB)) {
if(!pChr->subscribe(true, notifyCB)) {
/** Disconnect if subscribe failed */
pClient->disconnect();
return false;
}
}
else if(pChr->canIndicate()) {
/** Send false as first argument to subscribe to indications instead of notifications */
//if(!pChr->registerForNotify(notifyCB, false)) {
if(!pChr->subscribe(false, notifyCB)) {
/** Disconnect if subscribe failed */
pClient->disconnect();
return false;
}
}
}
else{
printf("DEAD service not found.\n");
}
pSvc = pClient->getService("BAAD");
if(pSvc) { /** make sure it's not null */
pChr = pSvc->getCharacteristic("F00D");
}
if(pChr) { /** make sure it's not null */
if(pChr->canRead()) {
printf("%s Value: %s\n",
pChr->getUUID().toString().c_str(),
pChr->readValue().c_str());
}
pDsc = pChr->getDescriptor(NimBLEUUID("C01D"));
if(pDsc) { /** make sure it's not null */
printf("Descriptor: %s Value: %s\n",
pDsc->getUUID().toString().c_str(),
pDsc->readValue().c_str());
}
if(pChr->canWrite()) {
if(pChr->writeValue("No tip!")) {
printf("Wrote new value to: %s\n", pChr->getUUID().toString().c_str());
}
else {
/** Disconnect if write failed */
pClient->disconnect();
return false;
}
if(pChr->canRead()) {
printf("The value of: %s is now: %s\n",
pChr->getUUID().toString().c_str(),
pChr->readValue().c_str());
}
}
/** registerForNotify() has been deprecated and replaced with subscribe() / unsubscribe().
* Subscribe parameter defaults are: notifications=true, notifyCallback=nullptr, response=true.
* Unsubscribe parameter defaults are: response=true.
*/
if(pChr->canNotify()) {
//if(!pChr->registerForNotify(notifyCB)) {
if(!pChr->subscribe(true, notifyCB)) {
/** Disconnect if subscribe failed */
pClient->disconnect();
return false;
}
}
else if(pChr->canIndicate()) {
/** Send false as first argument to subscribe to indications instead of notifications */
//if(!pChr->registerForNotify(notifyCB, false)) {
if(!pChr->subscribe(false, notifyCB)) {
/** Disconnect if subscribe failed */
pClient->disconnect();
return false;
}
}
}
else{
printf("BAAD service not found.\n");
}
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) {
doConnect = false;
/** Found a device we want to connect to, do it now */
if(connectToServer()) {
printf("Success! we should now be getting notifications, scanning for more!\n");
} else {
printf("Failed to connect, starting scan\n");
}
NimBLEDevice::getScan()->start(scanTime);
}
vTaskDelay(10/portTICK_PERIOD_MS);
}
vTaskDelete(NULL);
}
void app_main (void){
printf("Starting NimBLE Client\n");
/** Initialize NimBLE, no device name spcified as we are not advertising */
NimBLEDevice::init("");
/** Set the IO capabilities of the device, each option will trigger a different pairing method.
* BLE_HS_IO_KEYBOARD_ONLY - Passkey pairing
* BLE_HS_IO_DISPLAY_YESNO - Numeric comparison pairing
* BLE_HS_IO_NO_INPUT_OUTPUT - DEFAULT setting - just works pairing
*/
//NimBLEDevice::setSecurityIOCap(BLE_HS_IO_KEYBOARD_ONLY); // use passkey
//NimBLEDevice::setSecurityIOCap(BLE_HS_IO_DISPLAY_YESNO); //use numeric comparison
/** 2 different ways to set security - both calls achieve the same result.
* no bonding, no man in the middle protection, secure connections.
*
* These are the default values, only shown here for demonstration.
*/
//NimBLEDevice::setSecurityAuth(false, false, true);
NimBLEDevice::setSecurityAuth(/*BLE_SM_PAIR_AUTHREQ_BOND | BLE_SM_PAIR_AUTHREQ_MITM |*/ BLE_SM_PAIR_AUTHREQ_SC);
/** Optional: set the transmit power, default is -3db */
NimBLEDevice::setPower(9); // 9dbm
/** create new scan */
NimBLEScan* pScan = NimBLEDevice::getScan();
/** create a callback that gets called when advertisers are found */
pScan->setScanCallbacks (new scanCallbacks());
/** Set scan interval (how often) and window (how long) in milliseconds */
pScan->setInterval(400);
pScan->setWindow(100);
/** 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 milliseconds) 0 = forever
* Optional callback for when scanning stops.
*/
pScan->start(scanTime);
printf("Scanning for peripherals\n");
xTaskCreate(connectTask, "connectTask", 5000, NULL, 1, NULL);
}

View file

@ -1,3 +0,0 @@
PROJECT_NAME := NimBLE_Server
include $(IDF_PATH)/make/project.mk

View file

@ -1,4 +0,0 @@
#
# "main" pseudo-component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)

View file

@ -3,5 +3,4 @@
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
set(SUPPORTED_TARGETS esp32c3 esp32s3)
project(NimBLE_extended_client)

View file

@ -1,3 +0,0 @@
PROJECT_NAME := NimBLE_extended_client
include $(IDF_PATH)/make/project.mk

View file

@ -1,4 +0,0 @@
#
# "main" pseudo-component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)

View file

@ -1,15 +1,14 @@
/** NimBLE Extended Client Demo:
/**
* NimBLE Extended Client Demo:
*
* Demonstrates the Bluetooth 5.x client capabilities.
*
* Created: on April 2 2022
* Author: H2zero
*
*/
#include <NimBLEDevice.h>
extern "C" void app_main(void);
#include <NimBLEDevice.h>
#define SERVICE_UUID "ABCD"
#define CHARACTERISTIC_UUID "1234"
@ -18,54 +17,46 @@ static const NimBLEAdvertisedDevice* advDevice;
static bool doConnect = false;
static uint32_t scanTime = 10 * 1000; // In milliseconds, 0 = scan forever
/* Define the PHY's to use when connecting to peer devices, can be 1, 2, or all 3 (default).*/
/** 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 */
/** Define a class to handle the callbacks for client connection events */
class ClientCallbacks : public NimBLEClientCallbacks {
void onConnect(NimBLEClient* pClient) {
printf("Connected\n");
};
void onConnect(NimBLEClient* pClient) override { printf("Connected\n"); };
void onDisconnect(NimBLEClient* pClient, int reason) {
printf("%s Disconnected, reason = %d - Starting scan\n",
pClient->getPeerAddress().toString().c_str(), reason);
void onDisconnect(NimBLEClient* pClient, int reason) override {
printf("%s Disconnected, reason = %d - Starting scan\n", pClient->getPeerAddress().toString().c_str(), reason);
NimBLEDevice::getScan()->start(scanTime);
};
};
}
} clientCallbacks;
/* Define a class to handle the callbacks when advertisements are received */
/** Define a class to handle the callbacks when advertisements are received */
class scanCallbacks : public NimBLEScanCallbacks {
void onResult(const NimBLEAdvertisedDevice* advertisedDevice) {
void onResult(const NimBLEAdvertisedDevice* advertisedDevice) override {
printf("Advertised Device found: %s\n", advertisedDevice->toString().c_str());
if(advertisedDevice->isAdvertisingService(NimBLEUUID("ABCD")))
{
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*/
/** Save the device reference in a global for the client to use*/
advDevice = advertisedDevice;
/* stop scan before connecting */
/** stop scan before connecting */
NimBLEDevice::getScan()->stop();
}
}
/** Callback to process the results of the completed scan or restart it */
void onScanEnd(const NimBLEScanResults& results, int rc) {
printf("Scan Ended\n");
}
};
void onScanEnd(const NimBLEScanResults& results, int rc) override { printf("Scan Ended\n"); }
} scanCallbacks;
/* Handles the provisioning of clients and connects / interfaces with the server */
/** Handles the provisioning of clients and connects / interfaces with the server */
bool connectToServer() {
NimBLEClient* pClient = nullptr;
pClient = NimBLEDevice::createClient();
pClient->setClientCallbacks(new ClientCallbacks, false);
pClient->setClientCallbacks(&clientCallbacks, false);
/* Set the PHY's to use for this connection. This is a bitmask that represents the PHY's:
/**
* 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
@ -77,27 +68,22 @@ bool connectToServer() {
pClient->setConnectTimeout(10 * 1000);
if (!pClient->connect(advDevice)) {
/* Created a client but failed to connect, don't need to keep it as it has no data */
/** 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());
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 */
/** Now we can read/write/subscribe the characteristics 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());
@ -113,11 +99,37 @@ bool connectToServer() {
return true;
}
void connectTask (void * parameter){
/* Loop here until we find a device we want to connect to */
extern "C" void app_main(void) {
printf("Starting NimBLE Client\n");
/** Initialize NimBLE and set the device name */
NimBLEDevice::init("NimBLE Extended Client");
/** Create aNimBLE Scan instance and set the callbacks for scan events */
NimBLEScan* pScan = NimBLEDevice::getScan();
pScan->setScanCallbacks(&scanCallbacks);
/** 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 milliseconds) 0 = forever
* Optional callback for when scanning stops.
*/
pScan->start(scanTime);
printf("Scanning for peripherals\n");
/** 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 {
@ -129,35 +141,4 @@ void connectTask (void * parameter){
}
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->setScanCallbacks(new scanCallbacks());
/* 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 milliseconds) 0 = forever
* Optional callback for when scanning stops.
*/
pScan->start(scanTime);
printf("Scanning for peripherals\n");
}

View file

@ -3,5 +3,4 @@
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
set(SUPPORTED_TARGETS esp32c3 esp32s3 esp32c6 esp32h2 esp32c2)
project(NimBLE_extended_scan)

View file

@ -5,7 +5,6 @@
*
* Created: on November 28, 2024
* Author: H2zero
*
*/
#include <NimBLEDevice.h>
@ -13,18 +12,20 @@
static uint32_t scanTime = 10 * 1000; // In milliseconds, 0 = scan forever
static NimBLEScan::Phy scanPhy = NimBLEScan::Phy::SCAN_ALL;
// Define a class to handle the callbacks when advertisements are received
class scanCallbacks: public NimBLEScanCallbacks {
/** Define a class to handle the callbacks when advertisements are received */
class ScanCallbacks : public NimBLEScanCallbacks {
void onResult(const NimBLEAdvertisedDevice* advertisedDevice) {
printf("Advertised Device found: %s\n PHY1: %d\n PHY2: %d\n", advertisedDevice->toString().c_str(),
advertisedDevice->getPrimaryPhy(), advertisedDevice->getSecondaryPhy());
printf("Advertised Device found: %s\n PHY1: %d\n PHY2: %d\n",
advertisedDevice->toString().c_str(),
advertisedDevice->getPrimaryPhy(),
advertisedDevice->getSecondaryPhy());
}
// Callback to process the results of the completed scan or restart it
/** Callback to process the results of the completed scan or restart it */
void onScanEnd(const NimBLEScanResults& scanResults, int reason) {
printf("Scan Ended, reason: %d; found %d devices\n", reason, scanResults.getCount());
// Try Different PHY's
/** Try Different PHY's */
switch (scanPhy) {
case NimBLEScan::Phy::SCAN_ALL:
printf("Scanning only 1M PHY\n");
@ -44,25 +45,25 @@ class scanCallbacks: public NimBLEScanCallbacks {
pScan->setPhy(scanPhy);
pScan->start(scanTime);
}
} scanCb;
} scanCallbacks;
extern "C" void app_main(void) {
printf("Starting Extended Scanner\n");
// Initialize NimBLE, no device name specified as we are not advertising
NimBLEDevice::init("");
/** Initialize NimBLE and set the device name */
NimBLEDevice::init("NimBLE Extended Scanner");
NimBLEScan* pScan = NimBLEDevice::getScan();
// Set the callbacks that the scanner will call on events.
pScan->setScanCallbacks(&scanCb);
/** Set the callbacks that the scanner will call on events. */
pScan->setScanCallbacks(&scanCallbacks);
// Use active scanning to obtain scan response data from advertisers
/** Use active scanning to obtain scan response data from advertisers */
pScan->setActiveScan(true);
// Set the initial PHY's to scan on, default is SCAN_ALL
/** Set the initial PHY's to scan on, default is SCAN_ALL */
pScan->setPhy(scanPhy);
// Start scanning for scanTime, 0 = forever
/** Start scanning for scanTime */
pScan->start(scanTime);
printf("Scanning for peripherals\n");
}

View file

@ -3,5 +3,4 @@
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
set(SUPPORTED_TARGETS esp32c3 esp32s3)
project(NimBLE_extended_server)

View file

@ -1,3 +0,0 @@
PROJECT_NAME := NimBLE_extended_server
include $(IDF_PATH)/make/project.mk

View file

@ -1,4 +0,0 @@
#
# "main" pseudo-component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)

View file

@ -1,4 +1,5 @@
/** NimBLE Extended Server Demo:
/**
* NimBLE Extended Server Demo:
*
* Demonstrates the Bluetooth 5.x extended advertising capabilities.
*
@ -9,49 +10,46 @@
*
* Created: on April 2 2022
* Author: H2zero
*
*/
#include "NimBLEDevice.h"
#include "esp_sleep.h"
extern "C" void app_main(void);
#include <NimBLEDevice.h>
#include <esp_sleep.h>
#define SERVICE_UUID "ABCD"
#define CHARACTERISTIC_UUID "1234"
/* Time in milliseconds to advertise */
/** Time in milliseconds to advertise */
static uint32_t advTime = 5000;
/* Time to sleep between advertisements */
/** 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 */
/** 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,
/**
* 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 */
/** Handler class for server events */
class ServerCallbacks : public NimBLEServerCallbacks {
void onConnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo) {
void onConnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo) override {
printf("Client connected:: %s\n", connInfo.getAddress().toString().c_str());
};
}
void onDisconnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo, int reason) {
void onDisconnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo, int reason) override {
printf("Client disconnected - sleeping for %" PRIu32 " seconds\n", sleepSeconds);
esp_deep_sleep_start();
};
};
}
} serverCallbacks;
/* Callback class to handle advertising events */
class advertisingCallbacks: public NimBLEExtAdvertisingCallbacks {
void onStopped(NimBLEExtAdvertising* pAdv, int reason, uint8_t inst_id) {
/** Callback class to handle advertising events */
class AdvertisingCallbacks : public NimBLEExtAdvertisingCallbacks {
void onStopped(NimBLEExtAdvertising* pAdv, int reason, uint8_t instId) override {
/* Check the reason advertising stopped, don't sleep if client is connecting */
printf("Advertising instance %u stopped\n", inst_id);
printf("Advertising instance %u stopped\n", instId);
switch (reason) {
case 0:
printf("Client connecting\n");
@ -65,40 +63,42 @@ class advertisingCallbacks: public NimBLEExtAdvertisingCallbacks {
esp_deep_sleep_start();
}
};
} advertisingCallbacks;
extern "C"
void app_main(void) {
/** Initialize NimBLE and set the device name */
NimBLEDevice::init("Extended advertiser");
/* Create the server and add the services/characteristics/descriptors */
/** Create the server and add the services/characteristics/descriptors */
NimBLEServer* pServer = NimBLEDevice::createServer();
pServer->setCallbacks(new ServerCallbacks);
pServer->setCallbacks(&serverCallbacks);
NimBLEService* pService = pServer->createService(SERVICE_UUID);
NimBLECharacteristic *pCharacteristic = pService->createCharacteristic(CHARACTERISTIC_UUID,
NIMBLE_PROPERTY::READ |
NIMBLE_PROPERTY::WRITE |
NIMBLE_PROPERTY::NOTIFY);
NimBLECharacteristic* pCharacteristic =
pService->createCharacteristic(CHARACTERISTIC_UUID,
NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::NOTIFY);
pCharacteristic->setValue("Hello World");
/* Start the services */
/** Start the service */
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 */
/** Set the advertisement as connectable */
extAdv.setConnectable(true);
/* As per Bluetooth specification, extended advertising cannot be both scannable and connectable */
/** 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 (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"
@ -106,14 +106,15 @@ void app_main (void) {
"and is using CODED_PHY for long range."));
extAdv.setCompleteServices16({NimBLEUUID(SERVICE_UUID)});
extAdv.setName("Extended advertiser");
/* When extended advertising is enabled `NimBLEDevice::getAdvertising` returns a pointer to `NimBLEExtAdvertising */
/** 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);
/** Set the callbacks for advertising events */
pAdvertising->setCallbacks(&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.
@ -122,8 +123,8 @@ void app_main (void) {
* 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
/**
* 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)) {
@ -132,7 +133,7 @@ void app_main (void) {
printf("Failed to start advertising\n");
}
} else {
printf("Failed to register advertisment data\n");
printf("Failed to register advertisement data\n");
}
esp_sleep_enable_timer_wakeup(sleepSeconds * 1000000);

View file

@ -3,5 +3,4 @@
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
set(SUPPORTED_TARGETS esp32c3 esp32s3)
project(NimBLE_multi_advertiser)

View file

@ -1,3 +0,0 @@
PROJECT_NAME := NimBLE_multi_advertiser
include $(IDF_PATH)/make/project.mk

View file

@ -1,4 +0,0 @@
#
# "main" pseudo-component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)

View file

@ -1,4 +1,5 @@
/** NimBLE Multi Advertiser Demo:
/**
* NimBLE Multi Advertiser Demo:
*
* Demonstrates the Bluetooth 5.x extended advertising capabilities.
*
@ -9,53 +10,50 @@
*
* Created: on April 9 2022
* Author: H2zero
*
*/
#include "NimBLEDevice.h"
#include "esp_sleep.h"
extern "C" void app_main(void);
#include <NimBLEDevice.h>
#include <esp_sleep.h>
#define SERVICE_UUID "ABCD"
#define CHARACTERISTIC_UUID "1234"
/* Time in milliseconds to advertise */
/** Time in milliseconds to advertise */
static uint32_t advTime = 5000;
/* Time to sleep between advertisements */
/** 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 */
/** 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,
/**
* 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 */
/** Handler class for server events */
class ServerCallbacks : public NimBLEServerCallbacks {
void onConnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo) {
void onConnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo) override {
printf("Client connected: %s\n", connInfo.getAddress().toString().c_str());
};
}
void onDisconnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo, int reason) {
void onDisconnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo, int reason) override {
printf("Client disconnected\n");
// if still advertising we won't sleep yet.
if (!pServer->getAdvertising()->isAdvertising()) {
printf("Sleeping for %" PRIu32 " seconds\n", sleepTime);
esp_deep_sleep_start();
}
};
};
}
} serverCallbacks;
/* Callback class to handle advertising events */
class advCallbacks: public NimBLEExtAdvertisingCallbacks {
void onStopped(NimBLEExtAdvertising* pAdv, int reason, uint8_t inst_id) {
/** Callback class to handle advertising events */
class AdvCallbacks : public NimBLEExtAdvertisingCallbacks {
void onStopped(NimBLEExtAdvertising* pAdv, int reason, uint8_t instId) override {
/* Check the reason advertising stopped, don't sleep if client is connecting */
printf("Advertising instance %u stopped\n", inst_id);
printf("Advertising instance %u stopped\n", instId);
switch (reason) {
case 0:
printf(" client connecting\n");
@ -72,89 +70,89 @@ class advCallbacks: public NimBLEExtAdvertisingCallbacks {
bool m_updatedSR = false;
void onScanRequest(NimBLEExtAdvertising* pAdv, uint8_t inst_id, NimBLEAddress addr) {
printf("Scan request for instance %u\n", inst_id);
void onScanRequest(NimBLEExtAdvertising* pAdv, uint8_t instId, NimBLEAddress addr) override {
printf("Scan request for instance %u\n", instId);
// 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);
pAdv->setScanResponseData(instId, sr);
m_updatedSR = true;
}
}
};
} advCallbacks;
void app_main (void) {
extern "C" void app_main(void) {
/** Initialize NimBLE and set the device name */
NimBLEDevice::init("Multi advertiser");
/* Create a server for our legacy advertiser */
/** Create a server for our legacy advertiser */
NimBLEServer* pServer = NimBLEDevice::createServer();
pServer->setCallbacks(new ServerCallbacks);
pServer->setCallbacks(&serverCallbacks);
NimBLEService* pService = pServer->createService(SERVICE_UUID);
NimBLECharacteristic *pCharacteristic = pService->createCharacteristic(CHARACTERISTIC_UUID,
NIMBLE_PROPERTY::READ |
NIMBLE_PROPERTY::WRITE |
NIMBLE_PROPERTY::NOTIFY);
NimBLECharacteristic* pCharacteristic =
pService->createCharacteristic(CHARACTERISTIC_UUID,
NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::NOTIFY);
pCharacteristic->setValue("Hello World");
/* Start the service */
/** Start the service */
pService->start();
/* Create our multi advertising instances */
/** Create our multi advertising instances */
// extended scannable instance advertising on coded and 1m PHY's.
/** extended scannable instance advertising on coded and 1m PHY's. */
NimBLEExtAdvertisement extScannable(primaryPhy, secondaryPhy);
// Legacy advertising as a connectable device.
/** Legacy advertising as a connectable device. */
NimBLEExtAdvertisement legacyConnectable;
// Optional scan response data.
/** Optional scan response data. */
NimBLEExtAdvertisement legacyScanResponse;
/* As per Bluetooth specification, extended advertising cannot be both scannable and connectable */
/** As per Bluetooth specification, extended advertising cannot be both scannable and connectable */
extScannable.setScannable(true);
extScannable.setConnectable(false);
/* Set the initial data */
/** 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. */
/** Enable the scan response callback, we will use this to update the data. */
extScannable.enableScanRequestCallback(true);
/* Optional custom address for this advertisment. */
/** Optional custom address for this advertisment. */
legacyConnectable.setAddress(NimBLEAddress("DE:AD:BE:EF:BA:AD"));
/* Set the advertising data. */
/** Set the advertising data. */
legacyConnectable.setName("Legacy");
legacyConnectable.setCompleteServices16({NimBLEUUID(SERVICE_UUID)});
/* Set the legacy and connectable flags. */
/** Set the legacy and connectable flags. */
legacyConnectable.setLegacyAdvertising(true);
legacyConnectable.setConnectable(true);
/* Put some data in the scan response if desired. */
/** Put some data in the scan response if desired. */
legacyScanResponse.setServiceData(NimBLEUUID(SERVICE_UUID), "Legacy SR");
/* Get the advertising ready */
/** Get the advertising ready */
NimBLEExtAdvertising* pAdvertising = NimBLEDevice::getAdvertising();
/* Set the callbacks to handle advertising events */
pAdvertising->setCallbacks(new advCallbacks);
/** Set the callbacks to handle advertising events */
pAdvertising->setCallbacks(&advCallbacks);
/* Set instance data.
/**
* 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 ) &&
if (pAdvertising->setInstanceData(0, extScannable) && pAdvertising->setInstanceData(1, legacyConnectable) &&
pAdvertising->setScanResponseData(1, legacyScanResponse)) {
/*
* `NimBLEExtAdvertising::start` takes the advertisement instance ID to start
/**
* 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)) {
@ -163,7 +161,7 @@ void app_main (void) {
printf("Failed to start advertising\n");
}
} else {
printf("Failed to register advertisment data\n");
printf("Failed to register advertisement data\n");
}
esp_sleep_enable_timer_wakeup(sleepTime * 1000000);

View file

@ -9,21 +9,22 @@
*
* Created: on March 24 2020
* Author: H2zero
*
*/
#include "NimBLEDevice.h"
#include <NimBLEDevice.h>
static constexpr uint32_t scanTime = 30 * 1000; // 30 seconds scan time.
class scanCallbacks : public NimBLEScanCallbacks {
// Initial discovery, advertisement data only.
/** Initial discovery, advertisement data only. */
void onDiscovered(const NimBLEAdvertisedDevice* advertisedDevice) override {
printf("Discovered Device: %s\n", advertisedDevice->toString().c_str());
}
// If active scanning the result here will have the scan response data.
// If not active scanning then this will be the same as onDiscovered.
/**
* If active scanning the result here will have the scan response data.
* If not active scanning then this will be the same as onDiscovered.
*/
void onResult(const NimBLEAdvertisedDevice* advertisedDevice) override {
printf("Device result: %s\n", advertisedDevice->toString().c_str());
}
@ -32,7 +33,7 @@ class scanCallbacks : public NimBLEScanCallbacks {
printf("Scan ended reason = %d; restarting scan\n", reason);
NimBLEDevice::getScan()->start(scanTime, false, true);
}
} scanCallbacks; // create a callback class instance.
} scanCallbacks;
extern "C" void app_main() {
NimBLEDevice::init(""); // Initialize the device, you can specify a device name if you want.

View file

@ -1,3 +0,0 @@
PROJECT_NAME := NimBLE_Async_Client
include $(IDF_PATH)/make/project.mk

View file

@ -1,4 +0,0 @@
#
# "main" pseudo-component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)

View file

@ -6,7 +6,6 @@
*
* Created: on November 4, 2024
* Author: H2zero
*
*/
#include <NimBLEDevice.h>
@ -14,22 +13,23 @@
static constexpr uint32_t scanTimeMs = 5 * 1000;
class ClientCallbacks : public NimBLEClientCallbacks {
void onConnect(NimBLEClient* pClient) {
void onConnect(NimBLEClient* pClient) override {
printf("Connected to: %s\n", pClient->getPeerAddress().toString().c_str());
}
void onDisconnect(NimBLEClient* pClient, int reason) {
void onDisconnect(NimBLEClient* pClient, int reason) override {
printf("%s Disconnected, reason = %d - Starting scan\n", pClient->getPeerAddress().toString().c_str(), reason);
NimBLEDevice::getScan()->start(scanTimeMs);
}
} clientCB;
} clientCallbacks;
class scanCallbacks : public NimBLEScanCallbacks {
void onResult(const NimBLEAdvertisedDevice* advertisedDevice) {
class ScanCallbacks : public NimBLEScanCallbacks {
void onResult(const NimBLEAdvertisedDevice* advertisedDevice) override {
printf("Advertised Device found: %s\n", advertisedDevice->toString().c_str());
if (advertisedDevice->haveName() && advertisedDevice->getName() == "NimBLE-Server") {
printf("Found Our Device\n");
/** Async connections can be made directly in the scan callbacks */
auto pClient = NimBLEDevice::getDisconnectedClient();
if (!pClient) {
pClient = NimBLEDevice::createClient(advertisedDevice->getAddress());
@ -39,7 +39,7 @@ class scanCallbacks : public NimBLEScanCallbacks {
}
}
pClient->setClientCallbacks(&clientCB, false);
pClient->setClientCallbacks(&clientCallbacks, false);
if (!pClient->connect(true, true, false)) { // delete attributes, async connect, no MTU exchange
NimBLEDevice::deleteClient(pClient);
printf("Failed to connect\n");
@ -48,19 +48,19 @@ class scanCallbacks : public NimBLEScanCallbacks {
}
}
void onScanEnd(const NimBLEScanResults&, int reason) {
void onScanEnd(const NimBLEScanResults& results, int reason) override {
printf("Scan Ended\n");
NimBLEDevice::getScan()->start(scanTimeMs);
}
};
} scanCallbacks;
extern "C" void app_main(void) {
printf("Starting NimBLE Async Client\n");
NimBLEDevice::init("");
NimBLEDevice::init("Async-Client");
NimBLEDevice::setPower(3); /** +3db */
NimBLEScan* pScan = NimBLEDevice::getScan();
pScan->setScanCallbacks(new scanCallbacks());
pScan->setScanCallbacks(&scanCallbacks);
pScan->setInterval(45);
pScan->setWindow(15);
pScan->setActiveScan(true);

View file

@ -3,5 +3,4 @@
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
set(SUPPORTED_TARGETS esp32)
project(NimBLE_Client)

View file

@ -0,0 +1,305 @@
/** NimBLE_Client Demo:
*
* Demonstrates many of the available features of the NimBLE client library.
*
* Created: on March 24 2020
* Author: H2zero
*/
#include <NimBLEDevice.h>
static const NimBLEAdvertisedDevice* advDevice;
static bool doConnect = false;
static uint32_t scanTime = 5000; /** scan time in milliseconds, 0 = scan forever */
/** None of these are required as they will be handled by the library with defaults. **
** Remove as you see fit for your needs */
class ClientCallbacks : public NimBLEClientCallbacks {
void onConnect(NimBLEClient* pClient) override { printf("Connected\n"); }
void onDisconnect(NimBLEClient* pClient, int reason) override {
printf("%s Disconnected, reason = %d - Starting scan\n", pClient->getPeerAddress().toString().c_str(), reason);
NimBLEDevice::getScan()->start(scanTime, false, true);
}
/********************* Security handled here *********************/
void onPassKeyEntry(NimBLEConnInfo& connInfo) override {
printf("Server Passkey Entry\n");
/**
* This should prompt the user to enter the passkey displayed
* on the peer device.
*/
NimBLEDevice::injectPassKey(connInfo, 123456);
}
void onConfirmPasskey(NimBLEConnInfo& connInfo, uint32_t pass_key) override {
printf("The passkey YES/NO number: %" PRIu32 "\n", pass_key);
/** Inject false if passkeys don't match. */
NimBLEDevice::injectConfirmPasskey(connInfo, true);
}
/** Pairing process complete, we can check the results in connInfo */
void onAuthenticationComplete(NimBLEConnInfo& connInfo) override {
if (!connInfo.isEncrypted()) {
printf("Encrypt connection failed - disconnecting\n");
/** Find the client with the connection handle provided in connInfo */
NimBLEDevice::getClientByHandle(connInfo.getConnHandle())->disconnect();
return;
}
}
} clientCB;
/** Define a class to handle the callbacks when scan events are received */
class scanCallbacks : public NimBLEScanCallbacks {
void onResult(const NimBLEAdvertisedDevice* advertisedDevice) override {
printf("Advertised Device found: %s\n", advertisedDevice->toString().c_str());
if (advertisedDevice->isAdvertisingService(NimBLEUUID("DEAD"))) {
printf("Found Our Service\n");
/** stop scan before connecting */
NimBLEDevice::getScan()->stop();
/** Save the device reference in a global for the client to use*/
advDevice = advertisedDevice;
/** Ready to connect now */
doConnect = true;
}
}
/** Callback to process the results of the completed scan or restart it */
void onScanEnd(const NimBLEScanResults& results, int reason) override {
printf("Scan Ended, reason: %d, device count: %d; Restarting scan\n", reason, results.getCount());
NimBLEDevice::getScan()->start(scanTime, false, true);
}
} scanCB;
/** Notification / Indication receiving handler callback */
void notifyCB(NimBLERemoteCharacteristic* pRemoteCharacteristic, uint8_t* pData, size_t length, bool isNotify) {
std::string str = (isNotify == true) ? "Notification" : "Indication";
str += " from ";
str += pRemoteCharacteristic->getClient()->getPeerAddress().toString();
str += ": Service = " + pRemoteCharacteristic->getRemoteService()->getUUID().toString();
str += ", Characteristic = " + pRemoteCharacteristic->getUUID().toString();
str += ", Value = " + std::string((char*)pData, length);
printf("%s\n", str.c_str());
}
/** Handles the provisioning of clients and connects / interfaces with the server */
bool connectToServer() {
NimBLEClient* pClient = nullptr;
/** Check if we have a client we should reuse first **/
if (NimBLEDevice::getCreatedClientCount()) {
/**
* Special case when we already know this device, we send false as the
* second argument in connect() to prevent refreshing the service database.
* This saves considerable time and power.
*/
pClient = NimBLEDevice::getClientByPeerAddress(advDevice->getAddress());
if (pClient) {
if (!pClient->connect(advDevice, false)) {
printf("Reconnect failed\n");
return false;
}
printf("Reconnected client\n");
} else {
/**
* We don't already have a client that knows this device,
* check for a client that is disconnected that we can use.
*/
pClient = NimBLEDevice::getDisconnectedClient();
}
}
/** No client to reuse? Create a new one. */
if (!pClient) {
if (NimBLEDevice::getCreatedClientCount() >= NIMBLE_MAX_CONNECTIONS) {
printf("Max clients reached - no more connections available\n");
return false;
}
pClient = NimBLEDevice::createClient();
printf("New client created\n");
pClient->setClientCallbacks(&clientCB, false);
/**
* Set initial connection parameters:
* These settings are safe for 3 clients to connect reliably, can go faster if you have less
* connections. Timeout should be a multiple of the interval, minimum is 100ms.
* Min interval: 12 * 1.25ms = 15, Max interval: 12 * 1.25ms = 15, 0 latency, 150 * 10ms = 1500ms timeout
*/
pClient->setConnectionParams(12, 12, 0, 150);
/** Set how long we are willing to wait for the connection to complete (milliseconds), default is 30000. */
pClient->setConnectTimeout(5 * 1000);
if (!pClient->connect(advDevice)) {
/** 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;
}
}
if (!pClient->isConnected()) {
if (!pClient->connect(advDevice)) {
printf("Failed to connect\n");
return false;
}
}
printf("Connected to: %s RSSI: %d\n", pClient->getPeerAddress().toString().c_str(), pClient->getRssi());
/** Now we can read/write/subscribe the characteristics of the services we are interested in */
NimBLERemoteService* pSvc = nullptr;
NimBLERemoteCharacteristic* pChr = nullptr;
NimBLERemoteDescriptor* pDsc = nullptr;
pSvc = pClient->getService("DEAD");
if (pSvc) {
pChr = pSvc->getCharacteristic("BEEF");
}
if (pChr) {
if (pChr->canRead()) {
printf("%s Value: %s\n", pChr->getUUID().toString().c_str(), pChr->readValue().c_str());
}
if (pChr->canWrite()) {
if (pChr->writeValue("Tasty")) {
printf("Wrote new value to: %s\n", pChr->getUUID().toString().c_str());
} else {
pClient->disconnect();
return false;
}
if (pChr->canRead()) {
printf("The value of: %s is now: %s\n", pChr->getUUID().toString().c_str(), pChr->readValue().c_str());
}
}
if (pChr->canNotify()) {
if (!pChr->subscribe(true, notifyCB)) {
pClient->disconnect();
return false;
}
} else if (pChr->canIndicate()) {
/** Send false as first argument to subscribe to indications instead of notifications */
if (!pChr->subscribe(false, notifyCB)) {
pClient->disconnect();
return false;
}
}
} else {
printf("DEAD service not found.\n");
}
pSvc = pClient->getService("BAAD");
if (pSvc) {
pChr = pSvc->getCharacteristic("F00D");
if (pChr) {
if (pChr->canRead()) {
printf("%s Value: %s\n", pChr->getUUID().toString().c_str(), pChr->readValue().c_str());
}
pDsc = pChr->getDescriptor(NimBLEUUID("C01D"));
if (pDsc) {
printf("Descriptor: %s Value: %s\n", pDsc->getUUID().toString().c_str(), pDsc->readValue().c_str());
}
if (pChr->canWrite()) {
if (pChr->writeValue("No tip!")) {
printf("Wrote new value to: %s\n", pChr->getUUID().toString().c_str());
} else {
pClient->disconnect();
return false;
}
if (pChr->canRead()) {
printf("The value of: %s is now: %s\n", pChr->getUUID().toString().c_str(), pChr->readValue().c_str());
}
}
if (pChr->canNotify()) {
if (!pChr->subscribe(true, notifyCB)) {
pClient->disconnect();
return false;
}
} else if (pChr->canIndicate()) {
/** Send false as first argument to subscribe to indications instead of notifications */
if (!pChr->subscribe(false, notifyCB)) {
pClient->disconnect();
return false;
}
}
}
} else {
printf("BAAD service not found.\n");
}
printf("Done with this device!\n");
return true;
}
extern "C"
void app_main(void) {
printf("Starting NimBLE Client\n");
/** Initialize NimBLE and set the device name */
NimBLEDevice::init("NimBLE-Client");
/**
* Set the IO capabilities of the device, each option will trigger a different pairing method.
* BLE_HS_IO_KEYBOARD_ONLY - Passkey pairing
* BLE_HS_IO_DISPLAY_YESNO - Numeric comparison pairing
* BLE_HS_IO_NO_INPUT_OUTPUT - DEFAULT setting - just works pairing
*/
// NimBLEDevice::setSecurityIOCap(BLE_HS_IO_KEYBOARD_ONLY); // use passkey
// NimBLEDevice::setSecurityIOCap(BLE_HS_IO_DISPLAY_YESNO); //use numeric comparison
/**
* 2 different ways to set security - both calls achieve the same result.
* no bonding, no man in the middle protection, BLE secure connections.
* These are the default values, only shown here for demonstration.
*/
// NimBLEDevice::setSecurityAuth(false, false, true);
NimBLEDevice::setSecurityAuth(/*BLE_SM_PAIR_AUTHREQ_BOND | BLE_SM_PAIR_AUTHREQ_MITM |*/ BLE_SM_PAIR_AUTHREQ_SC);
/** Optional: set the transmit power */
NimBLEDevice::setPower(3); // 9dbm
NimBLEScan* pScan = NimBLEDevice::getScan();
/** Set the callbacks to call when scan events occur, no duplicates */
pScan->setScanCallbacks(&scanCB, false);
/** Set scan interval (how often) and window (how long) in milliseconds */
pScan->setInterval(100);
pScan->setWindow(100);
/**
* Active scan will gather scan response data from advertisers
* but will use more energy from both devices
*/
pScan->setActiveScan(true);
/** Start scanning for advertisers */
pScan->start(scanTime);
printf("Scanning for peripherals\n");
/** Loop here until we find a device we want to connect to */
for (;;) {
vTaskDelay(10 / portTICK_PERIOD_MS);
if (doConnect) {
doConnect = false;
/** Found a device we want to connect to, do it now */
if (connectToServer()) {
printf("Success! we should now be getting notifications, scanning for more!\n");
} else {
printf("Failed to connect, starting scan\n");
}
NimBLEDevice::getScan()->start(scanTime, false, true);
}
}
}

View file

@ -3,5 +3,4 @@
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
set(SUPPORTED_TARGETS esp32)
project(NimBLE_Server)

View file

@ -1,104 +1,94 @@
/** NimBLE_Server Demo:
/**
* NimBLE_Server Demo:
*
* Demonstrates many of the available features of the NimBLE server library.
*
* Created: on March 22 2020
* Author: H2zero
*
*/
#include "NimBLEDevice.h"
#include "NimBLELog.h"
#include <stdio.h>
extern "C" {void app_main(void);}
#include <NimBLEDevice.h>
static NimBLEServer* pServer;
/** None of these are required as they will be handled by the library with defaults. **
** Remove as you see fit for your needs */
class ServerCallbacks : public NimBLEServerCallbacks {
void onConnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo) {
void onConnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo) override {
printf("Client address: %s\n", connInfo.getAddress().toString().c_str());
/** We can use the connection handle here to ask for different connection parameters.
/**
* We can use the connection handle here to ask for different connection parameters.
* Args: connection handle, min connection interval, max connection interval
* latency, supervision timeout.
* Units; Min/Max Intervals: 1.25 millisecond increments.
* Latency: number of intervals allowed to skip.
* Timeout: 10 millisecond increments, try for 3x interval time for best results.
* Timeout: 10 millisecond increments.
*/
pServer->updateConnParams(connInfo.getConnHandle(), 24, 48, 0, 18);
};
}
void onDisconnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo, int reason) {
void onDisconnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo, int reason) override {
printf("Client disconnected - start advertising\n");
NimBLEDevice::startAdvertising();
};
}
void onMTUChange(uint16_t MTU, NimBLEConnInfo& connInfo) {
void onMTUChange(uint16_t MTU, NimBLEConnInfo& connInfo) override {
printf("MTU updated: %u for connection ID: %u\n", MTU, connInfo.getConnHandle());
pServer->updateConnParams(connInfo.getConnHandle(), 24, 48, 0, 60);
};
}
/********************* Security handled here **********************
****** Note: these are the same return values as defaults ********/
uint32_t onPassKeyDisplay(){
/********************* Security handled here *********************/
uint32_t onPassKeyDisplay() override {
printf("Server Passkey Display\n");
/** This should return a random 6 digit number for security
/**
* This should return a random 6 digit number for security
* or make your own static passkey as done here.
*/
return 123456;
};
}
void onConfirmasskeyN(NimBLEConnInfo& connInfo, uint32_t pass_key){
void onConfirmPassKey(NimBLEConnInfo& connInfo, uint32_t pass_key) override {
printf("The passkey YES/NO number: %" PRIu32 "\n", pass_key);
/** Inject false if passkeys don't match. */
NimBLEDevice::injectConfirmPasskey(connInfo, true);
};
}
void onAuthenticationComplete(NimBLEConnInfo& connInfo){
void onAuthenticationComplete(NimBLEConnInfo& connInfo) override {
/** Check that encryption was successful, if not we disconnect the client */
if (!connInfo.isEncrypted()) {
NimBLEDevice::getServer()->disconnect(connInfo.getConnHandle());
printf("Encrypt connection failed - disconnecting client\n");
return;
}
printf("Starting BLE work!");
};
};
printf("Secured connection to: %s\n", connInfo.getAddress().toString().c_str());
}
} serverCallbacks;
/** Handler class for characteristic actions */
class CharacteristicCallbacks : public NimBLECharacteristicCallbacks {
void onRead(NimBLECharacteristic* pCharacteristic, NimBLEConnInfo& connInfo) {
void onRead(NimBLECharacteristic* pCharacteristic, NimBLEConnInfo& connInfo) override {
printf("%s : onRead(), value: %s\n",
pCharacteristic->getUUID().toString().c_str(),
pCharacteristic->getValue().c_str());
}
void onWrite(NimBLECharacteristic* pCharacteristic, NimBLEConnInfo& connInfo) {
void onWrite(NimBLECharacteristic* pCharacteristic, NimBLEConnInfo& connInfo) override {
printf("%s : onWrite(), value: %s\n",
pCharacteristic->getUUID().toString().c_str(),
pCharacteristic->getValue().c_str());
}
/** Called before notification or indication is sent,
* the value can be changed here before sending if desired.
*/
void onNotify(NimBLECharacteristic* pCharacteristic) {
printf("Sending notification to clients\n");
}
/**
* The value returned in code is the NimBLE host return code.
*/
void onStatus(NimBLECharacteristic* pCharacteristic, int code) {
printf("Notification/Indication return code: %d, %s\n",
code, NimBLEUtils::returnCodeToString(code));
void onStatus(NimBLECharacteristic* pCharacteristic, int code) override {
printf("Notification/Indication return code: %d, %s\n", code, NimBLEUtils::returnCodeToString(code));
}
void onSubscribe(NimBLECharacteristic* pCharacteristic, NimBLEConnInfo& connInfo, uint16_t subValue) {
/** Peer subscribed to notifications/indications */
void onSubscribe(NimBLECharacteristic* pCharacteristic, NimBLEConnInfo& connInfo, uint16_t subValue) override {
std::string str = "Client ID: ";
str += connInfo.getConnHandle();
str += " Address: ";
@ -106,7 +96,7 @@ class CharacteristicCallbacks: public NimBLECharacteristicCallbacks {
if (subValue == 0) {
str += " Unsubscribed to ";
} else if (subValue == 1) {
str += " Subscribed to notfications for ";
str += " Subscribed to notifications for ";
} else if (subValue == 2) {
str += " Subscribed to indications for ";
} else if (subValue == 3) {
@ -116,26 +106,103 @@ class CharacteristicCallbacks: public NimBLECharacteristicCallbacks {
printf("%s\n", str.c_str());
}
};
} chrCallbacks;
/** Handler class for descriptor actions */
class DescriptorCallbacks : public NimBLEDescriptorCallbacks {
void onWrite(NimBLEDescriptor* pDescriptor, NimBLEConnInfo& connInfo) {
void onWrite(NimBLEDescriptor* pDescriptor, NimBLEConnInfo& connInfo) override {
std::string dscVal = pDescriptor->getValue();
printf("Descriptor witten value: %s\n", dscVal.c_str());
};
printf("Descriptor written value: %s\n", dscVal.c_str());
}
void onRead(NimBLEDescriptor* pDescriptor, NimBLEConnInfo& connInfo) {
void onRead(NimBLEDescriptor* pDescriptor, NimBLEConnInfo& connInfo) override {
printf("%s Descriptor read\n", pDescriptor->getUUID().toString().c_str());
};;
};
}
} dscCallbacks;
extern "C" void app_main(void) {
printf("Starting NimBLE Server\n");
/** Define callback instances globally to use for multiple Charateristics \ Descriptors */
static DescriptorCallbacks dscCallbacks;
static CharacteristicCallbacks chrCallbacks;
/** Initialize NimBLE and set the device name */
NimBLEDevice::init("NimBLE");
void notifyTask(void * parameter){
/**
* Set the IO capabilities of the device, each option will trigger a different pairing method.
* BLE_HS_IO_DISPLAY_ONLY - Passkey pairing
* BLE_HS_IO_DISPLAY_YESNO - Numeric comparison pairing
* BLE_HS_IO_NO_INPUT_OUTPUT - DEFAULT setting - just works pairing
*/
// NimBLEDevice::setSecurityIOCap(BLE_HS_IO_DISPLAY_ONLY); // use passkey
// NimBLEDevice::setSecurityIOCap(BLE_HS_IO_DISPLAY_YESNO); //use numeric comparison
/**
* 2 different ways to set security - both calls achieve the same result.
* no bonding, no man in the middle protection, BLE secure connections.
*
* These are the default values, only shown here for demonstration.
*/
// NimBLEDevice::setSecurityAuth(false, false, true);
NimBLEDevice::setSecurityAuth(/*BLE_SM_PAIR_AUTHREQ_BOND | BLE_SM_PAIR_AUTHREQ_MITM |*/ BLE_SM_PAIR_AUTHREQ_SC);
pServer = NimBLEDevice::createServer();
pServer->setCallbacks(&serverCallbacks);
NimBLEService* pDeadService = pServer->createService("DEAD");
NimBLECharacteristic* pBeefCharacteristic =
pDeadService->createCharacteristic("BEEF",
NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE |
/** Require a secure connection for read and write access */
NIMBLE_PROPERTY::READ_ENC | // only allow reading if paired / encrypted
NIMBLE_PROPERTY::WRITE_ENC // only allow writing if paired / encrypted
);
pBeefCharacteristic->setValue("Burger");
pBeefCharacteristic->setCallbacks(&chrCallbacks);
/**
* 2902 and 2904 descriptors are a special case, when createDescriptor is called with
* either of those uuid's it will create the associated class with the correct properties
* and sizes. However we must cast the returned reference to the correct type as the method
* only returns a pointer to the base NimBLEDescriptor class.
*/
NimBLE2904* pBeef2904 = pBeefCharacteristic->create2904();
pBeef2904->setFormat(NimBLE2904::FORMAT_UTF8);
pBeef2904->setCallbacks(&dscCallbacks);
NimBLEService* pBaadService = pServer->createService("BAAD");
NimBLECharacteristic* pFoodCharacteristic =
pBaadService->createCharacteristic("F00D", NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::NOTIFY);
pFoodCharacteristic->setValue("Fries");
pFoodCharacteristic->setCallbacks(&chrCallbacks);
/** Custom descriptor: Arguments are UUID, Properties, max length of the value in bytes */
NimBLEDescriptor* pC01Ddsc =
pFoodCharacteristic->createDescriptor("C01D",
NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::WRITE_ENC,
20);
pC01Ddsc->setValue("Send it back!");
pC01Ddsc->setCallbacks(&dscCallbacks);
/** Start the services when finished creating all Characteristics and Descriptors */
pDeadService->start();
pBaadService->start();
/** Create an advertising instance and add the services to the advertised data */
NimBLEAdvertising* pAdvertising = NimBLEDevice::getAdvertising();
pAdvertising->setName("NimBLE-Server");
pAdvertising->addServiceUUID(pDeadService->getUUID());
pAdvertising->addServiceUUID(pBaadService->getUUID());
/**
* If your device is battery powered you may consider setting scan response
* to false as it will extend battery life at the expense of less data sent.
*/
pAdvertising->enableScanResponse(true);
pAdvertising->start();
printf("Advertising Started\n");
/** Loop here and send notifications to connected peers */
for (;;) {
if (pServer->getConnectedCount()) {
NimBLEService* pSvc = pServer->getServiceByUUID("BAAD");
@ -148,95 +215,4 @@ void notifyTask(void * parameter){
}
vTaskDelay(2000 / portTICK_PERIOD_MS);
}
vTaskDelete(NULL);
}
void app_main(void) {
printf("Starting NimBLE Server\n");
/** sets device name */
NimBLEDevice::init("NimBLE");
/** Set the IO capabilities of the device, each option will trigger a different pairing method.
* BLE_HS_IO_DISPLAY_ONLY - Passkey pairing
* BLE_HS_IO_DISPLAY_YESNO - Numeric comparison pairing
* BLE_HS_IO_NO_INPUT_OUTPUT - DEFAULT setting - just works pairing
*/
//NimBLEDevice::setSecurityIOCap(BLE_HS_IO_DISPLAY_ONLY); // use passkey
//NimBLEDevice::setSecurityIOCap(BLE_HS_IO_DISPLAY_YESNO); //use numeric comparison
/** 2 different ways to set security - both calls achieve the same result.
* no bonding, no man in the middle protection, secure connections.
*
* These are the default values, only shown here for demonstration.
*/
//NimBLEDevice::setSecurityAuth(false, false, true);
NimBLEDevice::setSecurityAuth(/*BLE_SM_PAIR_AUTHREQ_BOND | BLE_SM_PAIR_AUTHREQ_MITM |*/ BLE_SM_PAIR_AUTHREQ_SC);
pServer = NimBLEDevice::createServer();
pServer->setCallbacks(new ServerCallbacks());
NimBLEService* pDeadService = pServer->createService("DEAD");
NimBLECharacteristic* pBeefCharacteristic = pDeadService->createCharacteristic(
"BEEF",
NIMBLE_PROPERTY::READ |
NIMBLE_PROPERTY::WRITE |
/** Require a secure connection for read and write access */
NIMBLE_PROPERTY::READ_ENC | // only allow reading if paired / encrypted
NIMBLE_PROPERTY::WRITE_ENC // only allow writing if paired / encrypted
);
pBeefCharacteristic->setValue("Burger");
pBeefCharacteristic->setCallbacks(&chrCallbacks);
/** 2902 and 2904 descriptors are a special case, when createDescriptor is called with
* either of those uuid's it will create the associated class with the correct properties
* and sizes. However we must cast the returned reference to the correct type as the method
* only returns a pointer to the base NimBLEDescriptor class.
*/
NimBLE2904* pBeef2904 = pBeefCharacteristic->create2904();
pBeef2904->setFormat(NimBLE2904::FORMAT_UTF8);
pBeef2904->setCallbacks(&dscCallbacks);
NimBLEService* pBaadService = pServer->createService("BAAD");
NimBLECharacteristic* pFoodCharacteristic = pBaadService->createCharacteristic(
"F00D",
NIMBLE_PROPERTY::READ |
NIMBLE_PROPERTY::WRITE |
NIMBLE_PROPERTY::NOTIFY
);
pFoodCharacteristic->setValue("Fries");
pFoodCharacteristic->setCallbacks(&chrCallbacks);
/** Custom descriptor: Arguments are UUID, Properties, max length in bytes of the value */
NimBLEDescriptor* pC01Ddsc = pFoodCharacteristic->createDescriptor(
"C01D",
NIMBLE_PROPERTY::READ |
NIMBLE_PROPERTY::WRITE|
NIMBLE_PROPERTY::WRITE_ENC, // only allow writing if paired / encrypted
20
);
pC01Ddsc->setValue("Send it back!");
pC01Ddsc->setCallbacks(&dscCallbacks);
/** Start the services when finished creating all Characteristics and Descriptors */
pDeadService->start();
pBaadService->start();
NimBLEAdvertising* pAdvertising = NimBLEDevice::getAdvertising();
/** Add the services to the advertisment data **/
pAdvertising->addServiceUUID(pDeadService->getUUID());
pAdvertising->addServiceUUID(pBaadService->getUUID());
/** If your device is battery powered you may consider setting scan response
* to false as it will extend battery life at the expense of less data sent.
*/
pAdvertising->enableScanResponse(true);
pAdvertising->start();
printf("Advertising Started\n");
xTaskCreate(notifyTask, "notifyTask", 5000, NULL, 1, NULL);
}

View file

@ -1,7 +0,0 @@
# The following lines of boilerplate have to be in your project's
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
set(SUPPORTED_TARGETS esp32)
project(NimBLE_server_get_client_name)

View file

@ -1,4 +0,0 @@
set(COMPONENT_SRCS "main.cpp")
set(COMPONENT_ADD_INCLUDEDIRS ".")
register_component()

View file

@ -1,83 +0,0 @@
/** NimBLE_server_get_client_name
*
* Demonstrates 2 ways for the server to read the device name from the connected client.
*
* Created: on June 24 2024
* Author: H2zero
*
*/
#include <NimBLEDevice.h>
// See the following for generating UUIDs:
// https://www.uuidgenerator.net/
#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8"
#define ENC_CHARACTERISTIC_UUID "9551f35b-8d91-42e4-8f7e-1358dfe272dc"
NimBLEServer* pServer;
class ServerCallbacks : public NimBLEServerCallbacks {
// Same as before but now includes the name parameter
void onConnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo, std::string& name) override {
printf("Client address: %s Name: %s\n", connInfo.getAddress().toString().c_str(), name.c_str());
}
// Same as before but now includes the name parameter
void onAuthenticationComplete(NimBLEConnInfo& connInfo, const std::string& name) override {
if (!connInfo.isEncrypted()) {
NimBLEDevice::getServer()->disconnect(connInfo.getConnHandle());
printf("Encrypt connection failed - disconnecting client\n");
return;
}
printf("Encrypted Client address: %s Name: %s\n", connInfo.getAddress().toString().c_str(), name.c_str());
}
};
extern "C" void app_main(void) {
printf("Starting BLE Server!\n");
NimBLEDevice::init("Connect to me!");
NimBLEDevice::setSecurityAuth(true, false, true); // Enable bonding to see full name on phones.
pServer = NimBLEDevice::createServer();
NimBLEService* pService = pServer->createService(SERVICE_UUID);
NimBLECharacteristic* pCharacteristic =
pService->createCharacteristic(CHARACTERISTIC_UUID, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE);
pCharacteristic->setValue("Hello World says NimBLE!");
NimBLECharacteristic* pEncCharacteristic = pService->createCharacteristic(
ENC_CHARACTERISTIC_UUID,
(NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::READ_ENC | NIMBLE_PROPERTY::WRITE_ENC));
pEncCharacteristic->setValue("Hello World says NimBLE Encrypted");
pService->start();
pServer->setCallbacks(new ServerCallbacks());
pServer->getPeerNameOnConnect(true); // Setting this will enable the onConnect callback that provides the name.
BLEAdvertising* pAdvertising = NimBLEDevice::getAdvertising();
pAdvertising->addServiceUUID(SERVICE_UUID);
pAdvertising->enableScanResponse(true);
pAdvertising->start();
printf("Advertising started, connect with your phone.\n");
while (true) {
auto clientCount = pServer->getConnectedCount();
if (clientCount) {
printf("Connected clients:\n");
for (auto i = 0; i < clientCount; ++i) {
NimBLEConnInfo peerInfo = pServer->getPeerInfo(i);
printf("Client address: %s Name: %s\n", peerInfo.getAddress().toString().c_str(),
// This function blocks until the name is retrieved, so cannot be used in callback functions.
pServer->getPeerName(peerInfo).c_str());
}
}
vTaskDelay(pdMS_TO_TICKS(10000));
}
}

View file

@ -1,12 +0,0 @@
# Override some defaults so BT stack is enabled
# in this example
#
# BT config
#
CONFIG_BT_ENABLED=y
CONFIG_BTDM_CTRL_MODE_BLE_ONLY=y
CONFIG_BTDM_CTRL_MODE_BR_EDR_ONLY=n
CONFIG_BTDM_CTRL_MODE_BTDM=n
CONFIG_BT_BLUEDROID_ENABLED=n
CONFIG_BT_NIMBLE_ENABLED=y

View file

@ -1,7 +0,0 @@
# The following lines of boilerplate have to be in your project's
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
set(SUPPORTED_TARGETS esp32)
project(BLE_client)

View file

@ -1,3 +0,0 @@
PROJECT_NAME := BLE_client
include $(IDF_PATH)/make/project.mk

View file

@ -1,4 +0,0 @@
set(COMPONENT_SRCS "main.cpp")
set(COMPONENT_ADD_INCLUDEDIRS ".")
register_component()

View file

@ -1,4 +0,0 @@
#
# "main" pseudo-component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)

View file

@ -1,214 +0,0 @@
/**
* A BLE client example that is rich in capabilities.
* There is a lot new capabilities implemented.
* author unknown
* updated by chegewara
* updated for NimBLE by H2zero
*/
/** NimBLE differences highlighted in comment blocks **/
/*******original********
#include "BLEDevice.h"
***********************/
#include "NimBLEDevice.h"
extern "C"{void app_main(void);}
// The remote service we wish to connect to.
static BLEUUID serviceUUID("4fafc201-1fb5-459e-8fcc-c5c9c331914b");
// The characteristic of the remote service we are interested in.
static BLEUUID charUUID("beb5483e-36e1-4688-b7f5-ea07361b26a8");
static bool doConnect = false;
static bool connected = false;
static bool doScan = false;
static BLERemoteCharacteristic* pRemoteCharacteristic;
static const BLEAdvertisedDevice* myDevice;
static void notifyCallback(
BLERemoteCharacteristic* pBLERemoteCharacteristic,
uint8_t* pData,
size_t length,
bool isNotify) {
printf("Notify callback for characteristic %s of data length %d data: %s\n",
pBLERemoteCharacteristic->getUUID().toString().c_str(),
length,
(char*)pData);
}
/** None of these are required as they will be handled by the library with defaults. **
** Remove as you see fit for your needs */
class MyClientCallback : public BLEClientCallbacks {
void onConnect(BLEClient* pclient) {
}
/** onDisconnect now takes a reason parameter to indicate the reason for disconnection
void onDisconnect(BLEClient* pclient) { */
void onDisconnect(BLEClient* pclient, int reason) {
connected = false;
printf("onDisconnect");
}
/***************** New - Security handled here ********************
****** Note: these are the same return values as defaults ********/
void onPassKeyEntry(NimBLEConnInfo& connInfo){
printf("Server Passkey Entry\n");
/** This should prompt the user to enter the passkey displayed
* on the peer device.
*/
NimBLEDevice::injectPassKey(connInfo, 123456);
};
void onConfirmPasskey(NimBLEConnInfo& connInfo, uint32_t pass_key){
printf("The passkey YES/NO number: %" PRIu32 "\n", pass_key);
/** Inject false if passkeys don't match. */
NimBLEDevice::injectConfirmPasskey(connInfo, true);
};
/** Pairing process complete, we can check the results in connInfo */
void onAuthenticationComplete(NimBLEConnInfo& connInfo){
if(!connInfo.isEncrypted()) {
printf("Encrypt connection failed - disconnecting\n");
/** Find the client with the connection handle provided in desc */
NimBLEDevice::getClientByHandle(connInfo.getConnHandle())->disconnect();
return;
}
}
/*******************************************************************/
};
bool connectToServer() {
printf("Forming a connection to %s\n", myDevice->getAddress().toString().c_str());
BLEClient* pClient = BLEDevice::createClient();
printf(" - Created client\n");
pClient->setClientCallbacks(new MyClientCallback());
// Connect to the remove BLE Server.
pClient->connect(myDevice); // if you pass BLEAdvertisedDevice instead of address, it will be recognized type of peer device address (public or private)
printf(" - Connected to server\n");
// Obtain a reference to the service we are after in the remote BLE server.
BLERemoteService* pRemoteService = pClient->getService(serviceUUID);
if (pRemoteService == nullptr) {
printf("Failed to find our service UUID: %s\n", serviceUUID.toString().c_str());
pClient->disconnect();
return false;
}
printf(" - Found our service\n");
// Obtain a reference to the characteristic in the service of the remote BLE server.
pRemoteCharacteristic = pRemoteService->getCharacteristic(charUUID);
if (pRemoteCharacteristic == nullptr) {
printf("Failed to find our characteristic UUID: %s\n", charUUID.toString().c_str());
pClient->disconnect();
return false;
}
printf(" - Found our characteristic\n");
// Read the value of the characteristic.
if(pRemoteCharacteristic->canRead()) {
std::string value = pRemoteCharacteristic->readValue();
printf("The characteristic value was: %s\n", value.c_str());
}
/** registerForNotify() has been removed and replaced with subscribe() / unsubscribe().
* Subscribe parameter defaults are: notifications=true, notifyCallback=nullptr, response=true.
* Unsubscribe parameter defaults are: response=true.
*/
if(pRemoteCharacteristic->canNotify()) {
//pRemoteCharacteristic->registerForNotify(notifyCallback);
pRemoteCharacteristic->subscribe(true, notifyCallback);
}
connected = true;
return true;
}
/**
* Scan for BLE servers and find the first one that advertises the service we are looking for.
*/
class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks {
/**
* Called for each advertising BLE server.
*/
/*** Only a reference to the advertised device is passed now
void onResult(BLEAdvertisedDevice advertisedDevice) { **/
void onResult(const BLEAdvertisedDevice* advertisedDevice) {
printf("BLE Advertised Device found: %s\n", advertisedDevice->toString().c_str());
// We have found a device, let us now see if it contains the service we are looking for.
/********************************************************************************
if (advertisedDevice.haveServiceUUID() && advertisedDevice.isAdvertisingService(serviceUUID)) {
********************************************************************************/
if (advertisedDevice->haveServiceUUID() && advertisedDevice->isAdvertisingService(serviceUUID)) {
BLEDevice::getScan()->stop();
/*******************************************************************
myDevice = new BLEAdvertisedDevice(advertisedDevice);
*******************************************************************/
myDevice = advertisedDevice; /** Just save the reference now, no need to copy the object */
doConnect = true;
doScan = true;
} // Found our server
} // onResult
}; // MyAdvertisedDeviceCallbacks
// This is the Arduino main loop function.
void connectTask (void * parameter){
for(;;) {
// If the flag "doConnect" is true then we have scanned for and found the desired
// BLE Server with which we wish to connect. Now we connect to it. Once we are
// connected we set the connected flag to be true.
if (doConnect == true) {
if (connectToServer()) {
printf("We are now connected to the BLE Server.\n");
} else {
printf("We have failed to connect to the server; there is nothin more we will do.\n");
}
doConnect = false;
}
// If we are connected to a peer BLE Server, update the characteristic each time we are reached
// with the current time since boot.
if (connected) {
char buf[256];
snprintf(buf, 256, "Time since boot: %lu", (unsigned long)(esp_timer_get_time() / 1000000ULL));
printf("Setting new characteristic value to %s\n", buf);
// Set the characteristic's value to be the array of bytes that is actually a string.
/*** Note: write value now returns true if successful, false otherwise - try again or disconnect ***/
pRemoteCharacteristic->writeValue((uint8_t*)buf, strlen(buf), false);
}else if(doScan){
BLEDevice::getScan()->start(0); // this is just eample to start scan after disconnect, most likely there is better way to do it
}
vTaskDelay(1000/portTICK_PERIOD_MS); // Delay a second between loops.
}
vTaskDelete(NULL);
} // End of loop
void app_main(void) {
printf("Starting BLE Client application...\n");
BLEDevice::init("");
// Retrieve a Scanner and set the callback we want to use to be informed when we
// have detected a new device. Specify that we want active scanning and start the
// scan to run for 5 seconds.
BLEScan* pBLEScan = BLEDevice::getScan();
pBLEScan->setScanCallbacks(new MyAdvertisedDeviceCallbacks());
pBLEScan->setInterval(1349);
pBLEScan->setWindow(449);
pBLEScan->setActiveScan(true);
xTaskCreate(connectTask, "connectTask", 5000, NULL, 1, NULL);
pBLEScan->start(5 * 1000, false);
} // End of setup.

View file

@ -1,12 +0,0 @@
# Override some defaults so BT stack is enabled
# in this example
#
# BT config
#
CONFIG_BT_ENABLED=y
CONFIG_BTDM_CTRL_MODE_BLE_ONLY=y
CONFIG_BTDM_CTRL_MODE_BR_EDR_ONLY=n
CONFIG_BTDM_CTRL_MODE_BTDM=n
CONFIG_BT_BLUEDROID_ENABLED=n
CONFIG_BT_NIMBLE_ENABLED=y

View file

@ -1,7 +0,0 @@
# The following lines of boilerplate have to be in your project's
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
set(SUPPORTED_TARGETS esp32)
project(BLE_notify)

View file

@ -1,3 +0,0 @@
PROJECT_NAME := BLE_notify
include $(IDF_PATH)/make/project.mk

View file

@ -1,4 +0,0 @@
set(COMPONENT_SRCS "main.cpp")
set(COMPONENT_ADD_INCLUDEDIRS ".")
register_component()

View file

@ -1,4 +0,0 @@
#
# "main" pseudo-component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)

View file

@ -1,164 +0,0 @@
/*
Video: https://www.youtube.com/watch?v=oCMOYS71NIU
Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleNotify.cpp
Ported to Arduino ESP32 by Evandro Copercini
updated by chegewara
Refactored back to IDF by H2zero
Create a BLE server that, once we receive a connection, will send periodic notifications.
The service advertises itself as: 4fafc201-1fb5-459e-8fcc-c5c9c331914b
And has a characteristic of: beb5483e-36e1-4688-b7f5-ea07361b26a8
The design of creating the BLE server is:
1. Create a BLE Server
2. Create a BLE Service
3. Create a BLE Characteristic on the Service
4. Create a BLE Descriptor on the characteristic
5. Start the service.
6. Start advertising.
A connect hander associated with the server starts a background task that performs notification
every couple of seconds.
*/
/** NimBLE differences highlighted in comment blocks **/
/*******original********
#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <BLE2902.h>
***********************/
#include <NimBLEDevice.h>
extern "C" {void app_main(void);}
BLEServer* pServer = NULL;
BLECharacteristic* pCharacteristic = NULL;
bool deviceConnected = false;
bool oldDeviceConnected = false;
uint32_t value = 0;
// See the following for generating UUIDs:
// https://www.uuidgenerator.net/
#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8"
/** None of these are required as they will be handled by the library with defaults. **
** Remove as you see fit for your needs */
class MyServerCallbacks: public BLEServerCallbacks {
void onConnect(BLEServer* pServer, BLEConnInfo& connInfo) {
deviceConnected = true;
};
void onDisconnect(BLEServer* pServer, BLEConnInfo& connInfo, int reason) {
deviceConnected = false;
}
/***************** New - Security handled here ********************
****** Note: these are the same return values as defaults ********/
uint32_t onPassKeyDisplay(){
printf("Server Passkey Display\n");
/** This should return a random 6 digit number for security
* or make your own static passkey as done here.
*/
return 123456;
};
void onConfirmPasskey(NimBLEConnInfo& connInfo, uint32_t pass_key){
printf("The passkey YES/NO number: %" PRIu32 "\n", pass_key);
/** Inject false if passkeys don't match. */
NimBLEDevice::injectConfirmPasskey(connInfo, true);
};
void onAuthenticationComplete(NimBLEConnInfo& connInfo){
/** Check that encryption was successful, if not we disconnect the client */
if(!connInfo.isEncrypted()) {
NimBLEDevice::getServer()->disconnect(connInfo.getConnHandle());
printf("Encrypt connection failed - disconnecting client\n");
return;
}
printf("Starting BLE work!");
};
/*******************************************************************/
};
void connectedTask (void * parameter){
for(;;) {
// notify changed value
if (deviceConnected) {
pCharacteristic->setValue((uint8_t*)&value, 4);
pCharacteristic->notify();
value++;
vTaskDelay(100/portTICK_PERIOD_MS); // bluetooth stack will go into congestion, if too many packets are sent
}
// disconnecting
if (!deviceConnected && oldDeviceConnected) {
vTaskDelay(500/portTICK_PERIOD_MS); // give the bluetooth stack the chance to get things ready
pServer->startAdvertising(); // restart advertising
printf("start advertising\n");
oldDeviceConnected = deviceConnected;
}
// connecting
if (deviceConnected && !oldDeviceConnected) {
// do stuff here on connecting
oldDeviceConnected = deviceConnected;
}
vTaskDelay(10/portTICK_PERIOD_MS); // Delay between loops to reset watchdog timer
}
vTaskDelete(NULL);
}
void app_main(void) {
// Create the BLE Device
BLEDevice::init("ESP32");
// Create the BLE Server
pServer = BLEDevice::createServer();
pServer->setCallbacks(new MyServerCallbacks());
// Create the BLE Service
BLEService *pService = pServer->createService(SERVICE_UUID);
// Create a BLE Characteristic
pCharacteristic = pService->createCharacteristic(
CHARACTERISTIC_UUID,
/******* Enum Type NIMBLE_PROPERTY now *******
BLECharacteristic::PROPERTY_READ |
BLECharacteristic::PROPERTY_WRITE |
BLECharacteristic::PROPERTY_NOTIFY |
BLECharacteristic::PROPERTY_INDICATE
);
**********************************************/
NIMBLE_PROPERTY::READ |
NIMBLE_PROPERTY::WRITE |
NIMBLE_PROPERTY::NOTIFY |
NIMBLE_PROPERTY::INDICATE
);
// Create a BLE Descriptor
/***************************************************
NOTE: DO NOT create a 2902 descriptor.
it will be created automatically if notifications
or indications are enabled on a characteristic.
pCharacteristic->addDescriptor(new BLE2902());
****************************************************/
// Start the service
pService->start();
// Start advertising
BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
pAdvertising->addServiceUUID(SERVICE_UUID);
pAdvertising->enableScanResponse(false);
/** This method had been removed **
pAdvertising->setMinPreferred(0x0); // set value to 0x00 to not advertise this parameter
**/
xTaskCreate(connectedTask, "connectedTask", 5000, NULL, 1, NULL);
BLEDevice::startAdvertising();
printf("Waiting a client connection to notify...\n");
}

View file

@ -1,12 +0,0 @@
# Override some defaults so BT stack is enabled
# in this example
#
# BT config
#
CONFIG_BT_ENABLED=y
CONFIG_BTDM_CTRL_MODE_BLE_ONLY=y
CONFIG_BTDM_CTRL_MODE_BR_EDR_ONLY=n
CONFIG_BTDM_CTRL_MODE_BTDM=n
CONFIG_BT_BLUEDROID_ENABLED=n
CONFIG_BT_NIMBLE_ENABLED=y

View file

@ -1,7 +0,0 @@
# The following lines of boilerplate have to be in your project's
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
set(SUPPORTED_TARGETS esp32)
project(BLE_scan)

View file

@ -1,3 +0,0 @@
PROJECT_NAME := BLE_scan
include $(IDF_PATH)/make/project.mk

View file

@ -1,4 +0,0 @@
set(COMPONENT_SRCS "main.cpp")
set(COMPONENT_ADD_INCLUDEDIRS ".")
register_component()

View file

@ -1,4 +0,0 @@
#
# "main" pseudo-component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)

View file

@ -1,52 +0,0 @@
/*
Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleScan.cpp
Ported to Arduino ESP32 by Evandro Copercini
Refactored back to IDF by H2zero
*/
/** NimBLE differences highlighted in comment blocks **/
/*******original********
#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEScan.h>
#include <BLEAdvertisedDevice.h>
***********************/
#include <NimBLEDevice.h>
extern "C"{void app_main(void);}
int scanTime = 5 * 1000; // In milliseconds, 0 = scan forever
BLEScan* pBLEScan;
class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks {
void onResult(const BLEAdvertisedDevice* advertisedDevice) {
printf("Advertised Device: %s \n", advertisedDevice->toString().c_str());
}
};
void scanTask (void * parameter){
for(;;) {
// put your main code here, to run repeatedly:
BLEScanResults foundDevices = pBLEScan->getResults(scanTime, false);
printf("Devices found: %d\n", foundDevices.getCount());
printf("Scan done!\n");
pBLEScan->clearResults(); // delete results fromBLEScan buffer to release memory
vTaskDelay(2000/portTICK_PERIOD_MS); // Delay a second between loops.
}
vTaskDelete(NULL);
}
void app_main(void) {
printf("Scanning...\n");
BLEDevice::init("");
pBLEScan = BLEDevice::getScan(); //create new scan
pBLEScan->setScanCallbacks(new MyAdvertisedDeviceCallbacks());
pBLEScan->setActiveScan(true); //active scan uses more power, but get results faster
pBLEScan->setInterval(100);
pBLEScan->setWindow(99); // less or equal setInterval value
xTaskCreate(scanTask, "scanTask", 5000, NULL, 1, NULL);
}

View file

@ -1,12 +0,0 @@
# Override some defaults so BT stack is enabled
# in this example
#
# BT config
#
CONFIG_BT_ENABLED=y
CONFIG_BTDM_CTRL_MODE_BLE_ONLY=y
CONFIG_BTDM_CTRL_MODE_BR_EDR_ONLY=n
CONFIG_BTDM_CTRL_MODE_BTDM=n
CONFIG_BT_BLUEDROID_ENABLED=n
CONFIG_BT_NIMBLE_ENABLED=y

View file

@ -1,7 +0,0 @@
# The following lines of boilerplate have to be in your project's
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
set(SUPPORTED_TARGETS esp32)
project(BLE_server)

View file

@ -1,3 +0,0 @@
PROJECT_NAME := BLE_server
include $(IDF_PATH)/make/project.mk

View file

@ -1,4 +0,0 @@
set(COMPONENT_SRCS "main.cpp")
set(COMPONENT_ADD_INCLUDEDIRS ".")
register_component()

View file

@ -1,4 +0,0 @@
#
# "main" pseudo-component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)

View file

@ -1,57 +0,0 @@
/*
Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleServer.cpp
Ported to Arduino ESP32 by Evandro Copercini
updates by chegewara
Refactored back to IDF by H2zero
*/
/** NimBLE differences highlighted in comment blocks **/
/*******original********
#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEServer.h>
***********************/
#include <NimBLEDevice.h>
extern "C"{void app_main(void);}
// See the following for generating UUIDs:
// https://www.uuidgenerator.net/
#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8"
void app_main(void) {
printf("Starting BLE work!\n");
BLEDevice::init("Long name works now");
BLEServer *pServer = BLEDevice::createServer();
BLEService *pService = pServer->createService(SERVICE_UUID);
BLECharacteristic *pCharacteristic = pService->createCharacteristic(
CHARACTERISTIC_UUID,
/***** Enum Type NIMBLE_PROPERTY now *****
BLECharacteristic::PROPERTY_READ |
BLECharacteristic::PROPERTY_WRITE
);
*****************************************/
NIMBLE_PROPERTY::READ |
NIMBLE_PROPERTY::WRITE
);
pCharacteristic->setValue("Hello World says Neil");
pService->start();
// BLEAdvertising *pAdvertising = pServer->getAdvertising(); // this still is working for backward compatibility
BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
pAdvertising->addServiceUUID(SERVICE_UUID);
pAdvertising->enableScanResponse(true);
/** These methods have been removed **
pAdvertising->setMinPreferred(0x06); // functions that help with iPhone connections issue
pAdvertising->setMinPreferred(0x12);
*/
BLEDevice::startAdvertising();
printf("Characteristic defined! Now you can read it in your phone!\n");
}

View file

@ -1,12 +0,0 @@
# Override some defaults so BT stack is enabled
# in this example
#
# BT config
#
CONFIG_BT_ENABLED=y
CONFIG_BTDM_CTRL_MODE_BLE_ONLY=y
CONFIG_BTDM_CTRL_MODE_BR_EDR_ONLY=n
CONFIG_BTDM_CTRL_MODE_BTDM=n
CONFIG_BT_BLUEDROID_ENABLED=n
CONFIG_BT_NIMBLE_ENABLED=y

View file

@ -1,7 +0,0 @@
# The following lines of boilerplate have to be in your project's
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
set(SUPPORTED_TARGETS esp32)
project(BLE_uart)

View file

@ -1,3 +0,0 @@
PROJECT_NAME := BLE_uart
include $(IDF_PATH)/make/project.mk

View file

@ -1,4 +0,0 @@
set(COMPONENT_SRCS "main.cpp")
set(COMPONENT_ADD_INCLUDEDIRS ".")
register_component()

View file

@ -1,4 +0,0 @@
#
# "main" pseudo-component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)

View file

@ -1,177 +0,0 @@
/*
Video: https://www.youtube.com/watch?v=oCMOYS71NIU
Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleNotify.cpp
Ported to Arduino ESP32 by Evandro Copercini
Refactored back to IDF by H2zero
Create a BLE server that, once we receive a connection, will send periodic notifications.
The service advertises itself as: 6E400001-B5A3-F393-E0A9-E50E24DCCA9E
Has a characteristic of: 6E400002-B5A3-F393-E0A9-E50E24DCCA9E - used for receiving data with "WRITE"
Has a characteristic of: 6E400003-B5A3-F393-E0A9-E50E24DCCA9E - used to send data with "NOTIFY"
The design of creating the BLE server is:
1. Create a BLE Server
2. Create a BLE Service
3. Create a BLE Characteristic on the Service
4. Create a BLE Descriptor on the characteristic
5. Start the service.
6. Start advertising.
In this example rxValue is the data received (only accessible inside that function).
And txValue is the data to be sent, in this example just a byte incremented every second.
*/
/** NimBLE differences highlighted in comment blocks **/
/*******original********
#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <BLE2902.h>
***********************/
#include <NimBLEDevice.h>
extern "C"{void app_main(void);}
BLEServer *pServer = NULL;
BLECharacteristic * pTxCharacteristic;
bool deviceConnected = false;
bool oldDeviceConnected = false;
uint8_t txValue = 0;
// See the following for generating UUIDs:
// https://www.uuidgenerator.net/
#define SERVICE_UUID "6E400001-B5A3-F393-E0A9-E50E24DCCA9E" // UART service UUID
#define CHARACTERISTIC_UUID_RX "6E400002-B5A3-F393-E0A9-E50E24DCCA9E"
#define CHARACTERISTIC_UUID_TX "6E400003-B5A3-F393-E0A9-E50E24DCCA9E"
/** None of these are required as they will be handled by the library with defaults. **
** Remove as you see fit for your needs */
class MyServerCallbacks: public BLEServerCallbacks {
void onConnect(BLEServer* pServer, BLEConnInfo& connInfo) {
deviceConnected = true;
};
void onDisconnect(BLEServer* pServer, BLEConnInfo& connInfo, int reason) {
deviceConnected = false;
}
/***************** New - Security handled here ********************
****** Note: these are the same return values as defaults ********/
uint32_t onPassKeyDisplay(){
printf("Server Passkey Display\n");
/** This should return a random 6 digit number for security
* or make your own static passkey as done here.
*/
return 123456;
};
void onConfirmPasskey(NimBLEConnInfo& connInfo, uint32_t pass_key){
printf("The passkey YES/NO number: %" PRIu32 "\n", pass_key);
/** Inject false if passkeys don't match. */
NimBLEDevice::injectConfirmPasskey(connInfo, true);
};
void onAuthenticationComplete(NimBLEConnInfo& connInfo){
/** Check that encryption was successful, if not we disconnect the client */
if(!connInfo.isEncrypted()) {
NimBLEDevice::getServer()->disconnect(connInfo.getConnHandle());
printf("Encrypt connection failed - disconnecting client\n");
return;
}
printf("Starting BLE work!");
};
/*******************************************************************/
};
class MyCallbacks: public BLECharacteristicCallbacks {
void onWrite(BLECharacteristic *pCharacteristic, BLEConnInfo& connInfo) {
std::string rxValue = pCharacteristic->getValue();
if (rxValue.length() > 0) {
printf("*********\n");
printf("Received Value: ");
for (int i = 0; i < rxValue.length(); i++)
printf("%d", rxValue[i]);
printf("\n*********\n");
}
}
};
void connectedTask (void * parameter){
for(;;) {
if (deviceConnected) {
pTxCharacteristic->setValue(&txValue, 1);
pTxCharacteristic->notify();
txValue++;
}
// disconnecting
if (!deviceConnected && oldDeviceConnected) {
pServer->startAdvertising(); // restart advertising
printf("start advertising\n");
oldDeviceConnected = deviceConnected;
}
// connecting
if (deviceConnected && !oldDeviceConnected) {
// do stuff here on connecting
oldDeviceConnected = deviceConnected;
}
vTaskDelay(10/portTICK_PERIOD_MS); // Delay between loops to reset watchdog timer
}
vTaskDelete(NULL);
}
void app_main(void) {
// Create the BLE Device
BLEDevice::init("UART Service");
// Create the BLE Server
pServer = BLEDevice::createServer();
pServer->setCallbacks(new MyServerCallbacks());
// Create the BLE Service
BLEService *pService = pServer->createService(SERVICE_UUID);
// Create a BLE Characteristic
pTxCharacteristic = pService->createCharacteristic(
CHARACTERISTIC_UUID_TX,
/******* Enum Type NIMBLE_PROPERTY now *******
BLECharacteristic::PROPERTY_NOTIFY
);
**********************************************/
NIMBLE_PROPERTY::NOTIFY
);
/***************************************************
NOTE: DO NOT create a 2902 descriptor
it will be created automatically if notifications
or indications are enabled on a characteristic.
pCharacteristic->addDescriptor(new BLE2902());
****************************************************/
BLECharacteristic * pRxCharacteristic = pService->createCharacteristic(
CHARACTERISTIC_UUID_RX,
/******* Enum Type NIMBLE_PROPERTY now *******
BLECharacteristic::PROPERTY_WRITE
);
*********************************************/
NIMBLE_PROPERTY::WRITE
);
pRxCharacteristic->setCallbacks(new MyCallbacks());
// Start the service
pService->start();
xTaskCreate(connectedTask, "connectedTask", 5000, NULL, 1, NULL);
// Start advertising
pServer->getAdvertising()->start();
printf("Waiting a client connection to notify...\n");
}

View file

@ -1,12 +0,0 @@
# Override some defaults so BT stack is enabled
# in this example
#
# BT config
#
CONFIG_BT_ENABLED=y
CONFIG_BTDM_CTRL_MODE_BLE_ONLY=y
CONFIG_BTDM_CTRL_MODE_BR_EDR_ONLY=n
CONFIG_BTDM_CTRL_MODE_BTDM=n
CONFIG_BT_BLUEDROID_ENABLED=n
CONFIG_BT_NIMBLE_ENABLED=y