initial commit
This commit is contained in:
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
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
|
||||
*/
|
||||
|
||||
/** NimBLE differences highlighted in comment blocks **/
|
||||
|
||||
/*******original********
|
||||
#include <BLEDevice.h>
|
||||
#include <BLEUtils.h>
|
||||
#include <BLEScan.h>
|
||||
#include <BLEAdvertisedDevice.h>
|
||||
#include "BLEEddystoneURL.h"
|
||||
#include "BLEEddystoneTLM.h"
|
||||
#include "BLEBeacon.h"
|
||||
***********************/
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#include <NimBLEDevice.h>
|
||||
#include <NimBLEAdvertisedDevice.h>
|
||||
#include "NimBLEEddystoneURL.h"
|
||||
#include "NimBLEEddystoneTLM.h"
|
||||
#include "NimBLEBeacon.h"
|
||||
|
||||
#define ENDIAN_CHANGE_U16(x) ((((x)&0xFF00) >> 8) + (((x)&0xFF) << 8))
|
||||
|
||||
int scanTime = 5; //In seconds
|
||||
BLEScan *pBLEScan;
|
||||
|
||||
class MyAdvertisedDeviceCallbacks : public BLEAdvertisedDeviceCallbacks
|
||||
{
|
||||
/*** Only a reference to the advertised device is passed now
|
||||
void onResult(BLEAdvertisedDevice advertisedDevice) { **/
|
||||
void onResult(BLEAdvertisedDevice *advertisedDevice)
|
||||
{
|
||||
if (advertisedDevice->haveName())
|
||||
{
|
||||
Serial.print("Device name: ");
|
||||
Serial.println(advertisedDevice->getName().c_str());
|
||||
Serial.println("");
|
||||
}
|
||||
|
||||
if (advertisedDevice->haveServiceUUID())
|
||||
{
|
||||
BLEUUID devUUID = advertisedDevice->getServiceUUID();
|
||||
Serial.print("Found ServiceUUID: ");
|
||||
Serial.println(devUUID.toString().c_str());
|
||||
Serial.println("");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (advertisedDevice->haveManufacturerData() == true)
|
||||
{
|
||||
std::string strManufacturerData = advertisedDevice->getManufacturerData();
|
||||
|
||||
uint8_t cManufacturerData[100];
|
||||
strManufacturerData.copy((char *)cManufacturerData, strManufacturerData.length(), 0);
|
||||
|
||||
if (strManufacturerData.length() == 25 && cManufacturerData[0] == 0x4C && cManufacturerData[1] == 0x00)
|
||||
{
|
||||
Serial.println("Found an iBeacon!");
|
||||
BLEBeacon oBeacon = BLEBeacon();
|
||||
oBeacon.setData(strManufacturerData);
|
||||
Serial.printf("iBeacon Frame\n");
|
||||
Serial.printf("ID: %04X Major: %d Minor: %d UUID: %s Power: %d\n", oBeacon.getManufacturerId(), ENDIAN_CHANGE_U16(oBeacon.getMajor()), ENDIAN_CHANGE_U16(oBeacon.getMinor()), oBeacon.getProximityUUID().toString().c_str(), oBeacon.getSignalPower());
|
||||
}
|
||||
else
|
||||
{
|
||||
Serial.println("Found another manufacturers beacon!");
|
||||
Serial.printf("strManufacturerData: %d ", strManufacturerData.length());
|
||||
for (int i = 0; i < strManufacturerData.length(); i++)
|
||||
{
|
||||
Serial.printf("[%X]", cManufacturerData[i]);
|
||||
}
|
||||
Serial.printf("\n");
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
BLEUUID eddyUUID = (uint16_t)0xfeaa;
|
||||
|
||||
if (advertisedDevice->getServiceUUID().equals(eddyUUID))
|
||||
{
|
||||
std::string serviceData = advertisedDevice->getServiceData(eddyUUID);
|
||||
if (serviceData[0] == 0x10)
|
||||
{
|
||||
Serial.println("Found an EddystoneURL beacon!");
|
||||
BLEEddystoneURL foundEddyURL = BLEEddystoneURL();
|
||||
|
||||
foundEddyURL.setData(serviceData);
|
||||
std::string bareURL = foundEddyURL.getURL();
|
||||
if (bareURL[0] == 0x00)
|
||||
{
|
||||
Serial.println("DATA-->");
|
||||
for (int idx = 0; idx < serviceData.length(); idx++)
|
||||
{
|
||||
Serial.printf("0x%08X ", serviceData[idx]);
|
||||
}
|
||||
Serial.println("\nInvalid Data");
|
||||
return;
|
||||
}
|
||||
|
||||
Serial.printf("Found URL: %s\n", foundEddyURL.getURL().c_str());
|
||||
Serial.printf("Decoded URL: %s\n", foundEddyURL.getDecodedURL().c_str());
|
||||
Serial.printf("TX power %d\n", foundEddyURL.getPower());
|
||||
Serial.println("\n");
|
||||
}
|
||||
else if (serviceData[0] == 0x20)
|
||||
{
|
||||
Serial.println("Found an EddystoneTLM beacon!");
|
||||
BLEEddystoneTLM foundEddyURL = BLEEddystoneTLM();
|
||||
foundEddyURL.setData(serviceData);
|
||||
|
||||
Serial.printf("Reported battery voltage: %dmV\n", foundEddyURL.getVolt());
|
||||
Serial.printf("Reported temperature from TLM class: %.2fC\n", (double)foundEddyURL.getTemp());
|
||||
int temp = (int)serviceData[5] + (int)(serviceData[4] << 8);
|
||||
float calcTemp = temp / 256.0f;
|
||||
Serial.printf("Reported temperature from data: %.2fC\n", calcTemp);
|
||||
Serial.printf("Reported advertise count: %d\n", foundEddyURL.getCount());
|
||||
Serial.printf("Reported time since last reboot: %ds\n", foundEddyURL.getTime());
|
||||
Serial.println("\n");
|
||||
Serial.print(foundEddyURL.toString().c_str());
|
||||
Serial.println("\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(115200);
|
||||
Serial.println("Scanning...");
|
||||
|
||||
BLEDevice::init("");
|
||||
pBLEScan = BLEDevice::getScan(); //create new scan
|
||||
pBLEScan->setAdvertisedDeviceCallbacks(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
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
// put your main code here, to run repeatedly:
|
||||
BLEScanResults foundDevices = pBLEScan->start(scanTime, false);
|
||||
Serial.print("Devices found: ");
|
||||
Serial.println(foundDevices.getCount());
|
||||
Serial.println("Scan done!");
|
||||
pBLEScan->clearResults(); // delete results fromBLEScan buffer to release memory
|
||||
delay(2000);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
## BLE Beacon Scanner
|
||||
|
||||
Initiates a BLE device scan.
|
||||
Checks if the discovered devices are
|
||||
- an iBeacon
|
||||
- an Eddystone TLM beacon
|
||||
- an Eddystone URL beacon
|
||||
|
||||
and sends the decoded beacon information over Serial log
|
||||
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
EddystoneTLM beacon for NimBLE by BeeGee based on https://github.com/pcbreflux/espressif/blob/master/esp32/arduino/sketchbook/ESP32_Eddystone_TLM_deepsleep/ESP32_Eddystone_TLM_deepsleep.ino
|
||||
EddystoneTLM frame specification https://github.com/google/eddystone/blob/master/eddystone-tlm/tlm-plain.md
|
||||
*/
|
||||
|
||||
/*
|
||||
Create a BLE server that will send periodic Eddystone URL frames.
|
||||
The design of creating the BLE server is:
|
||||
1. Create a BLE Server
|
||||
2. Create advertising data
|
||||
3. Start advertising.
|
||||
4. wait
|
||||
5. Stop advertising.
|
||||
6. deep sleep
|
||||
|
||||
*/
|
||||
|
||||
#include "NimBLEDevice.h"
|
||||
#include "NimBLEBeacon.h"
|
||||
#include "NimBLEAdvertising.h"
|
||||
#include "NimBLEEddystoneURL.h"
|
||||
|
||||
#include "sys/time.h"
|
||||
#include "esp_sleep.h"
|
||||
|
||||
#define GPIO_DEEP_SLEEP_DURATION 10 // sleep x seconds and then wake up
|
||||
|
||||
// UUID 1 128-Bit (may use linux tool uuidgen or random numbers via https://www.uuidgenerator.net/)
|
||||
#define BEACON_UUID "8ec76ea3-6668-48da-9866-75be8bc86f4d"
|
||||
|
||||
RTC_DATA_ATTR static time_t last; // remember last boot in RTC Memory
|
||||
RTC_DATA_ATTR static uint32_t bootcount; // remember number of boots in RTC Memory
|
||||
|
||||
BLEAdvertising *pAdvertising;
|
||||
struct timeval nowTimeStruct;
|
||||
|
||||
time_t lastTenth;
|
||||
|
||||
// Check
|
||||
// https://github.com/google/eddystone/blob/master/eddystone-tlm/tlm-plain.md
|
||||
// and http://www.hugi.scene.org/online/coding/hugi%2015%20-%20cmtadfix.htm
|
||||
// for the temperature value. It is a 8.8 fixed-point notation
|
||||
void setBeacon()
|
||||
{
|
||||
char beacon_data[25];
|
||||
uint16_t beconUUID = 0xFEAA;
|
||||
uint16_t volt = random(2800, 3700); // 3300mV = 3.3V
|
||||
float tempFloat = random(2000, 3100) / 100.0f;
|
||||
Serial.printf("Random temperature is %.2fC\n", tempFloat);
|
||||
int temp = (int)(tempFloat * 256); //(uint16_t)((float)23.00);
|
||||
Serial.printf("Converted to 8.8 format %0X%0X\n", (temp >> 8), (temp & 0xFF));
|
||||
|
||||
BLEAdvertisementData oAdvertisementData = BLEAdvertisementData();
|
||||
BLEAdvertisementData oScanResponseData = BLEAdvertisementData();
|
||||
|
||||
oScanResponseData.setFlags(0x06); // GENERAL_DISC_MODE 0x02 | BR_EDR_NOT_SUPPORTED 0x04
|
||||
oScanResponseData.setCompleteServices(BLEUUID(beconUUID));
|
||||
|
||||
beacon_data[0] = 0x20; // Eddystone Frame Type (Unencrypted Eddystone-TLM)
|
||||
beacon_data[1] = 0x00; // TLM version
|
||||
beacon_data[2] = (volt >> 8); // Battery voltage, 1 mV/bit i.e. 0xCE4 = 3300mV = 3.3V
|
||||
beacon_data[3] = (volt & 0xFF); //
|
||||
beacon_data[4] = (temp >> 8); // Beacon temperature
|
||||
beacon_data[5] = (temp & 0xFF); //
|
||||
beacon_data[6] = ((bootcount & 0xFF000000) >> 24); // Advertising PDU count
|
||||
beacon_data[7] = ((bootcount & 0xFF0000) >> 16); //
|
||||
beacon_data[8] = ((bootcount & 0xFF00) >> 8); //
|
||||
beacon_data[9] = (bootcount & 0xFF); //
|
||||
beacon_data[10] = ((lastTenth & 0xFF000000) >> 24); // Time since power-on or reboot as 0.1 second resolution counter
|
||||
beacon_data[11] = ((lastTenth & 0xFF0000) >> 16); //
|
||||
beacon_data[12] = ((lastTenth & 0xFF00) >> 8); //
|
||||
beacon_data[13] = (lastTenth & 0xFF); //
|
||||
|
||||
oScanResponseData.setServiceData(BLEUUID(beconUUID), std::string(beacon_data, 14));
|
||||
oAdvertisementData.setName("TLMBeacon");
|
||||
pAdvertising->setAdvertisementData(oAdvertisementData);
|
||||
pAdvertising->setScanResponseData(oScanResponseData);
|
||||
}
|
||||
|
||||
void setup()
|
||||
{
|
||||
|
||||
Serial.begin(115200);
|
||||
gettimeofday(&nowTimeStruct, NULL);
|
||||
|
||||
Serial.printf("start ESP32 %d\n", bootcount++);
|
||||
|
||||
Serial.printf("deep sleep (%lds since last reset, %lds since last boot)\n", nowTimeStruct.tv_sec, nowTimeStruct.tv_sec - last);
|
||||
|
||||
last = nowTimeStruct.tv_sec;
|
||||
lastTenth = nowTimeStruct.tv_sec * 10; // Time since last reset as 0.1 second resolution counter
|
||||
|
||||
// Create the BLE Device
|
||||
BLEDevice::init("TLMBeacon");
|
||||
|
||||
BLEDevice::setPower(ESP_PWR_LVL_N12);
|
||||
|
||||
pAdvertising = BLEDevice::getAdvertising();
|
||||
|
||||
setBeacon();
|
||||
// Start advertising
|
||||
pAdvertising->start();
|
||||
Serial.println("Advertizing started for 10s ...");
|
||||
delay(10000);
|
||||
pAdvertising->stop();
|
||||
Serial.printf("enter deep sleep for 10s\n");
|
||||
esp_deep_sleep(1000000LL * GPIO_DEEP_SLEEP_DURATION);
|
||||
Serial.printf("in deep sleep\n");
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
## Eddystone TLM beacon
|
||||
EddystoneTLM beacon by BeeGee based on
|
||||
[pcbreflux ESP32 Eddystone TLM deepsleep](https://github.com/pcbreflux/espressif/blob/master/esp32/arduino/sketchbook/ESP32_Eddystone_TLM_deepsleep/ESP32_Eddystone_TLM_deepsleep.ino)
|
||||
|
||||
[EddystoneTLM frame specification](https://github.com/google/eddystone/blob/master/eddystone-tlm/tlm-plain.md)
|
||||
|
||||
Create a BLE server that will send periodic Eddystone TLM frames.
|
||||
The design of creating the BLE server is:
|
||||
1. Create a BLE Server
|
||||
2. Create advertising data
|
||||
3. Start advertising.
|
||||
4. wait
|
||||
5. Stop advertising.
|
||||
6. deep sleep
|
||||
@@ -0,0 +1,185 @@
|
||||
/*
|
||||
EddystoneURL beacon for NimBLE by BeeGee
|
||||
EddystoneURL frame specification https://github.com/google/eddystone/blob/master/eddystone-url/README.md
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
Create a BLE server that will send periodic Eddystone URL frames.
|
||||
The design of creating the BLE server is:
|
||||
1. Create a BLE Server
|
||||
2. Create advertising data
|
||||
3. Start advertising.
|
||||
4. wait
|
||||
5. Stop advertising.
|
||||
6. deep sleep
|
||||
|
||||
*/
|
||||
|
||||
#include "NimBLEDevice.h"
|
||||
#include "NimBLEBeacon.h"
|
||||
#include "NimBLEEddystoneURL.h"
|
||||
|
||||
#include "sys/time.h"
|
||||
#include "esp_sleep.h"
|
||||
|
||||
#define GPIO_DEEP_SLEEP_DURATION 10 // sleep x seconds and then wake up
|
||||
|
||||
// UUID 1 128-Bit (may use linux tool uuidgen or random numbers via https://www.uuidgenerator.net/)
|
||||
#define BEACON_UUID "8ec76ea3-6668-48da-9866-75be8bc86f4d"
|
||||
|
||||
RTC_DATA_ATTR static time_t last; // remember last boot in RTC Memory
|
||||
RTC_DATA_ATTR static uint32_t bootcount; // remember number of boots in RTC Memory
|
||||
|
||||
BLEAdvertising *pAdvertising;
|
||||
struct timeval now;
|
||||
|
||||
static const char *eddystone_url_prefix_subs[] = {
|
||||
"http://www.",
|
||||
"https://www.",
|
||||
"http://",
|
||||
"https://",
|
||||
"urn:uuid:",
|
||||
NULL
|
||||
};
|
||||
|
||||
static const char *eddystone_url_suffix_subs[] = {
|
||||
".com/",
|
||||
".org/",
|
||||
".edu/",
|
||||
".net/",
|
||||
".info/",
|
||||
".biz/",
|
||||
".gov/",
|
||||
".com",
|
||||
".org",
|
||||
".edu",
|
||||
".net",
|
||||
".info",
|
||||
".biz",
|
||||
".gov",
|
||||
NULL
|
||||
};
|
||||
|
||||
static int string_begin_with(const char *str, const char *prefix)
|
||||
{
|
||||
int prefix_len = strlen(prefix);
|
||||
if (strncmp(prefix, str, prefix_len) == 0)
|
||||
{
|
||||
return prefix_len;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void setBeacon()
|
||||
{
|
||||
BLEAdvertisementData oAdvertisementData = BLEAdvertisementData();
|
||||
BLEAdvertisementData oScanResponseData = BLEAdvertisementData();
|
||||
|
||||
const char url[] = "https://d.giesecke.tk";
|
||||
|
||||
int scheme_len, ext_len = 1, i, idx, url_idx;
|
||||
char *ret_data;
|
||||
int url_len = strlen(url);
|
||||
|
||||
ret_data = (char *)calloc(1, url_len + 13);
|
||||
|
||||
ret_data[0] = 2; // Len
|
||||
ret_data[1] = 0x01; // Type Flags
|
||||
ret_data[2] = 0x06; // GENERAL_DISC_MODE 0x02 | BR_EDR_NOT_SUPPORTED 0x04
|
||||
ret_data[3] = 3; // Len
|
||||
ret_data[4] = 0x03; // Type 16-Bit UUID
|
||||
ret_data[5] = 0xAA; // Eddystone UUID 2 -> 0xFEAA LSB
|
||||
ret_data[6] = 0xFE; // Eddystone UUID 1 MSB
|
||||
ret_data[7] = 19; // Length of Beacon Data
|
||||
ret_data[8] = 0x16; // Type Service Data
|
||||
ret_data[9] = 0xAA; // Eddystone UUID 2 -> 0xFEAA LSB
|
||||
ret_data[10] = 0xFE; // Eddystone UUID 1 MSB
|
||||
ret_data[11] = 0x10; // Eddystone Frame Type
|
||||
ret_data[12] = 0xF4; // Beacons TX power at 0m
|
||||
|
||||
i = 0, idx = 13, url_idx = 0;
|
||||
|
||||
//replace prefix
|
||||
scheme_len = 0;
|
||||
while (eddystone_url_prefix_subs[i] != NULL)
|
||||
{
|
||||
if ((scheme_len = string_begin_with(url, eddystone_url_prefix_subs[i])) > 0)
|
||||
{
|
||||
ret_data[idx] = i;
|
||||
idx++;
|
||||
url_idx += scheme_len;
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
while (url_idx < url_len)
|
||||
{
|
||||
i = 0;
|
||||
ret_data[idx] = url[url_idx];
|
||||
ext_len = 1;
|
||||
while (eddystone_url_suffix_subs[i] != NULL)
|
||||
{
|
||||
if ((ext_len = string_begin_with(&url[url_idx], eddystone_url_suffix_subs[i])) > 0)
|
||||
{
|
||||
ret_data[idx] = i;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
ext_len = 1; //inc 1
|
||||
}
|
||||
i++;
|
||||
}
|
||||
url_idx += ext_len;
|
||||
idx++;
|
||||
}
|
||||
ret_data[7] = idx - 8;
|
||||
|
||||
Serial.printf("struct size %d url size %d reported len %d\n",
|
||||
url_len + 13,
|
||||
url_len, ret_data[7]);
|
||||
|
||||
Serial.printf("URL in data %s\n", &ret_data[13]);
|
||||
|
||||
std::string eddyStoneData(ret_data);
|
||||
|
||||
oAdvertisementData.addData(eddyStoneData);
|
||||
oScanResponseData.setName("MeBeacon");
|
||||
pAdvertising->setAdvertisementData(oAdvertisementData);
|
||||
pAdvertising->setScanResponseData(oScanResponseData);
|
||||
}
|
||||
|
||||
void setup()
|
||||
{
|
||||
|
||||
Serial.begin(115200);
|
||||
gettimeofday(&now, NULL);
|
||||
|
||||
Serial.printf("start ESP32 %d\n", bootcount++);
|
||||
|
||||
Serial.printf("deep sleep (%lds since last reset, %lds since last boot)\n", now.tv_sec, now.tv_sec - last);
|
||||
|
||||
last = now.tv_sec;
|
||||
|
||||
// Create the BLE Device
|
||||
BLEDevice::init("MeBeacon");
|
||||
|
||||
BLEDevice::setPower(ESP_PWR_LVL_N12);
|
||||
|
||||
pAdvertising = BLEDevice::getAdvertising();
|
||||
|
||||
setBeacon();
|
||||
// Start advertising
|
||||
pAdvertising->start();
|
||||
Serial.println("Advertizing started...");
|
||||
delay(10000);
|
||||
pAdvertising->stop();
|
||||
Serial.printf("enter deep sleep\n");
|
||||
esp_deep_sleep(1000000LL * GPIO_DEEP_SLEEP_DURATION);
|
||||
Serial.printf("in deep sleep\n");
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
## Eddystone URL beacon
|
||||
EddystoneURL beacon by BeeGee based on
|
||||
[pcbreflux ESP32 Eddystone URL deepsleep](https://github.com/pcbreflux/espressif/tree/master/esp32/arduino/sketchbook/ESP32_Eddystone_URL_deepsleep)
|
||||
|
||||
[EddystoneURL frame specification](https://github.com/google/eddystone/blob/master/eddystone-url/README.md)
|
||||
|
||||
Create a BLE server that will send periodic Eddystone URL frames.
|
||||
The design of creating the BLE server is:
|
||||
1. Create a BLE Server
|
||||
2. Create advertising data
|
||||
3. Start advertising.
|
||||
4. wait
|
||||
5. Stop advertising.
|
||||
6. deep sleep
|
||||
392
lib/NimBLE-Arduino/examples/NimBLE_Client/NimBLE_Client.ino
Normal file
392
lib/NimBLE-Arduino/examples/NimBLE_Client/NimBLE_Client.ino
Normal file
@@ -0,0 +1,392 @@
|
||||
|
||||
/** NimBLE_Server Demo:
|
||||
*
|
||||
* Demonstrates many of the available features of the NimBLE client library.
|
||||
*
|
||||
* Created: on March 24 2020
|
||||
* Author: H2zero
|
||||
*
|
||||
*/
|
||||
|
||||
#include <NimBLEDevice.h>
|
||||
|
||||
void scanEndedCB(NimBLEScanResults results);
|
||||
|
||||
static NimBLEAdvertisedDevice* advDevice;
|
||||
|
||||
static bool doConnect = false;
|
||||
static uint32_t scanTime = 0; /** 0 = scan forever */
|
||||
|
||||
|
||||
/** None of these are required as they will be handled by the library with defaults. **
|
||||
** Remove as you see fit for your needs */
|
||||
class ClientCallbacks : public NimBLEClientCallbacks {
|
||||
void onConnect(NimBLEClient* pClient) {
|
||||
Serial.println("Connected");
|
||||
/** After connection we should change the parameters if we don't need fast response times.
|
||||
* These settings are 150ms interval, 0 latency, 450ms timout.
|
||||
* Timeout should be a multiple of the interval, minimum is 100ms.
|
||||
* I find a multiple of 3-5 * the interval works best for quick response/reconnect.
|
||||
* Min interval: 120 * 1.25ms = 150, Max interval: 120 * 1.25ms = 150, 0 latency, 60 * 10ms = 600ms timeout
|
||||
*/
|
||||
pClient->updateConnParams(120,120,0,60);
|
||||
};
|
||||
|
||||
void onDisconnect(NimBLEClient* pClient) {
|
||||
Serial.print(pClient->getPeerAddress().toString().c_str());
|
||||
Serial.println(" Disconnected - Starting scan");
|
||||
NimBLEDevice::getScan()->start(scanTime, scanEndedCB);
|
||||
};
|
||||
|
||||
/** Called when the peripheral requests a change to the connection parameters.
|
||||
* Return true to accept and apply them or false to reject and keep
|
||||
* the currently used parameters. Default will return true.
|
||||
*/
|
||||
bool onConnParamsUpdateRequest(NimBLEClient* pClient, const ble_gap_upd_params* params) {
|
||||
if(params->itvl_min < 24) { /** 1.25ms units */
|
||||
return false;
|
||||
} else if(params->itvl_max > 40) { /** 1.25ms units */
|
||||
return false;
|
||||
} else if(params->latency > 2) { /** Number of intervals allowed to skip */
|
||||
return false;
|
||||
} else if(params->supervision_timeout > 100) { /** 10ms units */
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
/********************* Security handled here **********************
|
||||
****** Note: these are the same return values as defaults ********/
|
||||
uint32_t onPassKeyRequest(){
|
||||
Serial.println("Client Passkey Request");
|
||||
/** return the passkey to send to the server */
|
||||
return 123456;
|
||||
};
|
||||
|
||||
bool onConfirmPIN(uint32_t pass_key){
|
||||
Serial.print("The passkey YES/NO number: ");
|
||||
Serial.println(pass_key);
|
||||
/** Return false if passkeys don't match. */
|
||||
return true;
|
||||
};
|
||||
|
||||
/** Pairing process complete, we can check the results in ble_gap_conn_desc */
|
||||
void onAuthenticationComplete(ble_gap_conn_desc* desc){
|
||||
if(!desc->sec_state.encrypted) {
|
||||
Serial.println("Encrypt connection failed - disconnecting");
|
||||
/** Find the client with the connection handle provided in desc */
|
||||
NimBLEDevice::getClientByID(desc->conn_handle)->disconnect();
|
||||
return;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
/** Define a class to handle the callbacks when advertisments are received */
|
||||
class AdvertisedDeviceCallbacks: public NimBLEAdvertisedDeviceCallbacks {
|
||||
|
||||
void onResult(NimBLEAdvertisedDevice* advertisedDevice) {
|
||||
Serial.print("Advertised Device found: ");
|
||||
Serial.println(advertisedDevice->toString().c_str());
|
||||
if(advertisedDevice->isAdvertisingService(NimBLEUUID("DEAD")))
|
||||
{
|
||||
Serial.println("Found Our Service");
|
||||
/** 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;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
/** 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 ";
|
||||
/** NimBLEAddress and NimBLEUUID have std::string operators */
|
||||
str += std::string(pRemoteCharacteristic->getRemoteService()->getClient()->getPeerAddress());
|
||||
str += ": Service = " + std::string(pRemoteCharacteristic->getRemoteService()->getUUID());
|
||||
str += ", Characteristic = " + std::string(pRemoteCharacteristic->getUUID());
|
||||
str += ", Value = " + std::string((char*)pData, length);
|
||||
Serial.println(str.c_str());
|
||||
}
|
||||
|
||||
/** Callback to process the results of the last scan or restart it */
|
||||
void scanEndedCB(NimBLEScanResults results){
|
||||
Serial.println("Scan Ended");
|
||||
}
|
||||
|
||||
|
||||
/** 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::getClientListSize()) {
|
||||
/** 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)) {
|
||||
Serial.println("Reconnect failed");
|
||||
return false;
|
||||
}
|
||||
Serial.println("Reconnected client");
|
||||
}
|
||||
/** We don't already have a client that knows this device,
|
||||
* we will check for a client that is disconnected that we can use.
|
||||
*/
|
||||
else {
|
||||
pClient = NimBLEDevice::getDisconnectedClient();
|
||||
}
|
||||
}
|
||||
|
||||
/** No client to reuse? Create a new one. */
|
||||
if(!pClient) {
|
||||
if(NimBLEDevice::getClientListSize() >= NIMBLE_MAX_CONNECTIONS) {
|
||||
Serial.println("Max clients reached - no more connections available");
|
||||
return false;
|
||||
}
|
||||
|
||||
pClient = NimBLEDevice::createClient();
|
||||
|
||||
Serial.println("New client created");
|
||||
|
||||
pClient->setClientCallbacks(&clientCB, false);
|
||||
/** Set initial connection parameters: These settings are 15ms interval, 0 latency, 120ms timout.
|
||||
* These settings are safe for 3 clients to connect reliably, can go faster if you have less
|
||||
* connections. Timeout should be a multiple of the interval, minimum is 100ms.
|
||||
* Min interval: 12 * 1.25ms = 15, Max interval: 12 * 1.25ms = 15, 0 latency, 51 * 10ms = 510ms timeout
|
||||
*/
|
||||
pClient->setConnectionParams(12,12,0,51);
|
||||
/** Set how long we are willing to wait for the connection to complete (seconds), default is 30. */
|
||||
pClient->setConnectTimeout(5);
|
||||
|
||||
|
||||
if (!pClient->connect(advDevice)) {
|
||||
/** Created a client but failed to connect, don't need to keep it as it has no data */
|
||||
NimBLEDevice::deleteClient(pClient);
|
||||
Serial.println("Failed to connect, deleted client");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if(!pClient->isConnected()) {
|
||||
if (!pClient->connect(advDevice)) {
|
||||
Serial.println("Failed to connect");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Serial.print("Connected to: ");
|
||||
Serial.println(pClient->getPeerAddress().toString().c_str());
|
||||
Serial.print("RSSI: ");
|
||||
Serial.println(pClient->getRssi());
|
||||
|
||||
/** Now we can read/write/subscribe the charateristics of the services we are interested in */
|
||||
NimBLERemoteService* pSvc = nullptr;
|
||||
NimBLERemoteCharacteristic* pChr = nullptr;
|
||||
NimBLERemoteDescriptor* pDsc = nullptr;
|
||||
|
||||
pSvc = pClient->getService("DEAD");
|
||||
if(pSvc) { /** make sure it's not null */
|
||||
pChr = pSvc->getCharacteristic("BEEF");
|
||||
|
||||
if(pChr) { /** make sure it's not null */
|
||||
if(pChr->canRead()) {
|
||||
Serial.print(pChr->getUUID().toString().c_str());
|
||||
Serial.print(" Value: ");
|
||||
Serial.println(pChr->readValue().c_str());
|
||||
}
|
||||
|
||||
if(pChr->canWrite()) {
|
||||
if(pChr->writeValue("Tasty")) {
|
||||
Serial.print("Wrote new value to: ");
|
||||
Serial.println(pChr->getUUID().toString().c_str());
|
||||
}
|
||||
else {
|
||||
/** Disconnect if write failed */
|
||||
pClient->disconnect();
|
||||
return false;
|
||||
}
|
||||
|
||||
if(pChr->canRead()) {
|
||||
Serial.print("The value of: ");
|
||||
Serial.print(pChr->getUUID().toString().c_str());
|
||||
Serial.print(" is now: ");
|
||||
Serial.println(pChr->readValue().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
/** registerForNotify() has been deprecated and replaced with subscribe() / unsubscribe().
|
||||
* Subscribe parameter defaults are: notifications=true, notifyCallback=nullptr, response=false.
|
||||
* Unsubscribe parameter defaults are: response=false.
|
||||
*/
|
||||
if(pChr->canNotify()) {
|
||||
//if(!pChr->registerForNotify(notifyCB)) {
|
||||
if(!pChr->subscribe(true, notifyCB)) {
|
||||
/** Disconnect if subscribe failed */
|
||||
pClient->disconnect();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if(pChr->canIndicate()) {
|
||||
/** Send false as first argument to subscribe to indications instead of notifications */
|
||||
//if(!pChr->registerForNotify(notifyCB, false)) {
|
||||
if(!pChr->subscribe(false, notifyCB)) {
|
||||
/** Disconnect if subscribe failed */
|
||||
pClient->disconnect();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
Serial.println("DEAD service not found.");
|
||||
}
|
||||
|
||||
pSvc = pClient->getService("BAAD");
|
||||
if(pSvc) { /** make sure it's not null */
|
||||
pChr = pSvc->getCharacteristic("F00D");
|
||||
|
||||
if(pChr) { /** make sure it's not null */
|
||||
if(pChr->canRead()) {
|
||||
Serial.print(pChr->getUUID().toString().c_str());
|
||||
Serial.print(" Value: ");
|
||||
Serial.println(pChr->readValue().c_str());
|
||||
}
|
||||
|
||||
pDsc = pChr->getDescriptor(NimBLEUUID("C01D"));
|
||||
if(pDsc) { /** make sure it's not null */
|
||||
Serial.print("Descriptor: ");
|
||||
Serial.print(pDsc->getUUID().toString().c_str());
|
||||
Serial.print(" Value: ");
|
||||
Serial.println(pDsc->readValue().c_str());
|
||||
}
|
||||
|
||||
if(pChr->canWrite()) {
|
||||
if(pChr->writeValue("No tip!")) {
|
||||
Serial.print("Wrote new value to: ");
|
||||
Serial.println(pChr->getUUID().toString().c_str());
|
||||
}
|
||||
else {
|
||||
/** Disconnect if write failed */
|
||||
pClient->disconnect();
|
||||
return false;
|
||||
}
|
||||
|
||||
if(pChr->canRead()) {
|
||||
Serial.print("The value of: ");
|
||||
Serial.print(pChr->getUUID().toString().c_str());
|
||||
Serial.print(" is now: ");
|
||||
Serial.println(pChr->readValue().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
/** registerForNotify() has been deprecated and replaced with subscribe() / unsubscribe().
|
||||
* Subscribe parameter defaults are: notifications=true, notifyCallback=nullptr, response=false.
|
||||
* Unsubscribe parameter defaults are: response=false.
|
||||
*/
|
||||
if(pChr->canNotify()) {
|
||||
//if(!pChr->registerForNotify(notifyCB)) {
|
||||
if(!pChr->subscribe(true, notifyCB)) {
|
||||
/** Disconnect if subscribe failed */
|
||||
pClient->disconnect();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if(pChr->canIndicate()) {
|
||||
/** Send false as first argument to subscribe to indications instead of notifications */
|
||||
//if(!pChr->registerForNotify(notifyCB, false)) {
|
||||
if(!pChr->subscribe(false, notifyCB)) {
|
||||
/** Disconnect if subscribe failed */
|
||||
pClient->disconnect();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
Serial.println("BAAD service not found.");
|
||||
}
|
||||
|
||||
Serial.println("Done with this device!");
|
||||
return true;
|
||||
}
|
||||
|
||||
void setup (){
|
||||
Serial.begin(115200);
|
||||
Serial.println("Starting NimBLE Client");
|
||||
/** Initialize NimBLE, no device name spcified as we are not advertising */
|
||||
NimBLEDevice::init("");
|
||||
|
||||
/** Set the IO capabilities of the device, each option will trigger a different pairing method.
|
||||
* BLE_HS_IO_KEYBOARD_ONLY - Passkey pairing
|
||||
* BLE_HS_IO_DISPLAY_YESNO - Numeric comparison pairing
|
||||
* 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(ESP_PWR_LVL_P9); /** +9db */
|
||||
|
||||
/** Optional: set any devices you don't want to get advertisments from */
|
||||
// NimBLEDevice::addIgnored(NimBLEAddress ("aa:bb:cc:dd:ee:ff"));
|
||||
|
||||
/** create new scan */
|
||||
NimBLEScan* pScan = NimBLEDevice::getScan();
|
||||
|
||||
/** create a callback that gets called when advertisers are found */
|
||||
pScan->setAdvertisedDeviceCallbacks(new AdvertisedDeviceCallbacks());
|
||||
|
||||
/** Set scan interval (how often) and window (how long) in milliseconds */
|
||||
pScan->setInterval(45);
|
||||
pScan->setWindow(15);
|
||||
|
||||
/** Active scan will gather scan response data from advertisers
|
||||
* but will use more energy from both devices
|
||||
*/
|
||||
pScan->setActiveScan(true);
|
||||
/** Start scanning for advertisers for the scan time specified (in seconds) 0 = forever
|
||||
* Optional callback for when scanning stops.
|
||||
*/
|
||||
pScan->start(scanTime, scanEndedCB);
|
||||
}
|
||||
|
||||
|
||||
void loop (){
|
||||
/** Loop here until we find a device we want to connect to */
|
||||
while(!doConnect){
|
||||
delay(1);
|
||||
}
|
||||
|
||||
doConnect = false;
|
||||
|
||||
/** Found a device we want to connect to, do it now */
|
||||
if(connectToServer()) {
|
||||
Serial.println("Success! we should now be getting notifications, scanning for more!");
|
||||
} else {
|
||||
Serial.println("Failed to connect, starting scan");
|
||||
}
|
||||
|
||||
NimBLEDevice::getScan()->start(scanTime,scanEndedCB);
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/** Example of continuous scanning for BLE advertisements.
|
||||
* This example will scan forever while consuming as few resources as possible
|
||||
* and report all advertisments on the serial monitor.
|
||||
*
|
||||
* Created: on January 31 2021
|
||||
* Author: H2zero
|
||||
*
|
||||
*/
|
||||
|
||||
#include "NimBLEDevice.h"
|
||||
|
||||
NimBLEScan* pBLEScan;
|
||||
|
||||
class MyAdvertisedDeviceCallbacks: public NimBLEAdvertisedDeviceCallbacks {
|
||||
void onResult(NimBLEAdvertisedDevice* advertisedDevice) {
|
||||
Serial.printf("Advertised Device: %s \n", advertisedDevice->toString().c_str());
|
||||
}
|
||||
};
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Serial.println("Scanning...");
|
||||
|
||||
/** *Optional* Sets the filtering mode used by the scanner in the BLE controller.
|
||||
*
|
||||
* Can be one of:
|
||||
* CONFIG_BTDM_SCAN_DUPL_TYPE_DEVICE (0) (default)
|
||||
* Filter by device address only, advertisements from the same address will be reported only once.
|
||||
*
|
||||
* CONFIG_BTDM_SCAN_DUPL_TYPE_DATA (1)
|
||||
* Filter by data only, advertisements with the same data will only be reported once,
|
||||
* even from different addresses.
|
||||
*
|
||||
* CONFIG_BTDM_SCAN_DUPL_TYPE_DATA_DEVICE (2)
|
||||
* Filter by address and data, advertisements from the same address will be reported only once,
|
||||
* except if the data in the advertisement has changed, then it will be reported again.
|
||||
*
|
||||
* Can only be used BEFORE calling NimBLEDevice::init.
|
||||
*/
|
||||
NimBLEDevice::setScanFilterMode(CONFIG_BTDM_SCAN_DUPL_TYPE_DEVICE);
|
||||
|
||||
/** *Optional* Sets the scan filter cache size in the BLE controller.
|
||||
* When the number of duplicate advertisements seen by the controller
|
||||
* reaches this value it will clear the cache and start reporting previously
|
||||
* seen devices. The larger this number, the longer time between repeated
|
||||
* device reports. Range 10 - 1000. (default 20)
|
||||
*
|
||||
* Can only be used BEFORE calling NimBLEDevice::init.
|
||||
*/
|
||||
NimBLEDevice::setScanDuplicateCacheSize(200);
|
||||
|
||||
NimBLEDevice::init("");
|
||||
|
||||
pBLEScan = NimBLEDevice::getScan(); //create new scan
|
||||
// Set the callback for when devices are discovered, no duplicates.
|
||||
pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks(), false);
|
||||
pBLEScan->setActiveScan(true); // Set active scanning, this will get more data from the advertiser.
|
||||
pBLEScan->setInterval(97); // How often the scan occurs / switches channels; in milliseconds,
|
||||
pBLEScan->setWindow(37); // How long to scan during the interval; in milliseconds.
|
||||
pBLEScan->setMaxResults(0); // do not store the scan results, use callback only.
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// If an error occurs that stops the scan, it will be restarted here.
|
||||
if(pBLEScan->isScanning() == false) {
|
||||
// Start scan with: duration = 0 seconds(forever), no scan end callback, not a continuation of a previous scan.
|
||||
pBLEScan->start(0, nullptr, false);
|
||||
}
|
||||
|
||||
delay(2000);
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* NimBLE_Scan_Whitelist demo
|
||||
*
|
||||
* Created May 16, 2021
|
||||
* Author: h2zero
|
||||
*/
|
||||
|
||||
#include <NimBLEDevice.h>
|
||||
|
||||
int scanTime = 5; //In seconds
|
||||
NimBLEScan* pBLEScan;
|
||||
|
||||
class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks {
|
||||
void onResult(BLEAdvertisedDevice* advertisedDevice) {
|
||||
Serial.printf("Advertised Device: %s \n", advertisedDevice->toString().c_str());
|
||||
/*
|
||||
* Here we add the device scanned to the whitelist based on service data but any
|
||||
* advertised data can be used for your preffered data.
|
||||
*/
|
||||
if (advertisedDevice->haveServiceData()) {
|
||||
/* If this is a device with data we want to capture, add it to the whitelist */
|
||||
if (advertisedDevice->getServiceData(NimBLEUUID("AABB")) != "") {
|
||||
Serial.printf("Adding %s to whitelist\n", std::string(advertisedDevice->getAddress()).c_str());
|
||||
NimBLEDevice::whiteListAdd(advertisedDevice->getAddress());
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Serial.println("Scanning...");
|
||||
|
||||
NimBLEDevice::init("");
|
||||
pBLEScan = NimBLEDevice::getScan();
|
||||
pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());
|
||||
pBLEScan->setActiveScan(true);
|
||||
pBLEScan->setInterval(100);
|
||||
pBLEScan->setFilterPolicy(BLE_HCI_SCAN_FILT_NO_WL);
|
||||
pBLEScan->setWindow(99);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
NimBLEScanResults foundDevices = pBLEScan->start(scanTime, false);
|
||||
Serial.print("Devices found: ");
|
||||
Serial.println(foundDevices.getCount());
|
||||
Serial.println("Scan done!");
|
||||
|
||||
Serial.println("Whitelist contains:");
|
||||
for (auto i=0; i<NimBLEDevice::getWhiteListCount(); ++i) {
|
||||
Serial.println(NimBLEDevice::getWhiteListAddress(i).toString().c_str());
|
||||
}
|
||||
|
||||
/*
|
||||
* If we have addresses in the whitelist enable the filter unless no devices were found
|
||||
* then scan without so we can find any new devices that we want.
|
||||
*/
|
||||
if (NimBLEDevice::getWhiteListCount() > 0) {
|
||||
if (foundDevices.getCount() == 0) {
|
||||
pBLEScan->setFilterPolicy(BLE_HCI_SCAN_FILT_NO_WL);
|
||||
} else {
|
||||
pBLEScan->setFilterPolicy(BLE_HCI_SCAN_FILT_USE_WL);
|
||||
}
|
||||
}
|
||||
pBLEScan->clearResults();
|
||||
delay(2000);
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
/** NimBLE_Secure_Client Demo:
|
||||
*
|
||||
* This example demonstrates the secure passkey protected conenction and communication between an esp32 server and an esp32 client.
|
||||
* Please note that esp32 stores auth info in nvs memory. After a successful connection it is possible that a passkey change will be ineffective.
|
||||
* To avoid this clear the memory of the esp32's between security testings. esptool.py is capable of this, example: esptool.py --port /dev/ttyUSB0 erase_flash.
|
||||
*
|
||||
* Created: on Jan 08 2021
|
||||
* Author: mblasee
|
||||
*/
|
||||
|
||||
#include <NimBLEDevice.h>
|
||||
|
||||
class ClientCallbacks : public NimBLEClientCallbacks
|
||||
{
|
||||
uint32_t onPassKeyRequest()
|
||||
{
|
||||
Serial.println("Client Passkey Request");
|
||||
/** return the passkey to send to the server */
|
||||
/** Change this to be different from NimBLE_Secure_Server if you want to test what happens on key mismatch */
|
||||
return 123456;
|
||||
};
|
||||
};
|
||||
static ClientCallbacks clientCB;
|
||||
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(115200);
|
||||
Serial.println("Starting NimBLE Client");
|
||||
|
||||
NimBLEDevice::init("");
|
||||
NimBLEDevice::setPower(ESP_PWR_LVL_P9);
|
||||
NimBLEDevice::setSecurityAuth(true, true, true);
|
||||
NimBLEDevice::setSecurityIOCap(BLE_HS_IO_KEYBOARD_ONLY);
|
||||
NimBLEScan *pScan = NimBLEDevice::getScan();
|
||||
NimBLEScanResults results = pScan->start(5);
|
||||
|
||||
NimBLEUUID serviceUuid("ABCD");
|
||||
|
||||
for (int i = 0; i < results.getCount(); i++)
|
||||
{
|
||||
NimBLEAdvertisedDevice device = results.getDevice(i);
|
||||
Serial.println(device.getName().c_str());
|
||||
|
||||
if (device.isAdvertisingService(serviceUuid))
|
||||
{
|
||||
NimBLEClient *pClient = NimBLEDevice::createClient();
|
||||
pClient->setClientCallbacks(&clientCB, false);
|
||||
|
||||
if (pClient->connect(&device))
|
||||
{
|
||||
pClient->secureConnection();
|
||||
NimBLERemoteService *pService = pClient->getService(serviceUuid);
|
||||
if (pService != nullptr)
|
||||
{
|
||||
NimBLERemoteCharacteristic *pNonSecureCharacteristic = pService->getCharacteristic("1234");
|
||||
|
||||
if (pNonSecureCharacteristic != nullptr)
|
||||
{
|
||||
// Testing to read a non secured characteristic, you should be able to read this even if you have mismatching passkeys.
|
||||
std::string value = pNonSecureCharacteristic->readValue();
|
||||
// print or do whatever you need with the value
|
||||
Serial.println(value.c_str());
|
||||
}
|
||||
|
||||
NimBLERemoteCharacteristic *pSecureCharacteristic = pService->getCharacteristic("1235");
|
||||
|
||||
if (pSecureCharacteristic != nullptr)
|
||||
{
|
||||
// Testing to read a secured characteristic, you should be able to read this only if you have matching passkeys, otherwise you should
|
||||
// get an error like this. E NimBLERemoteCharacteristic: "<< readValue rc=261"
|
||||
// This means you are trying to do something without the proper permissions.
|
||||
std::string value = pSecureCharacteristic->readValue();
|
||||
// print or do whatever you need with the value
|
||||
Serial.println(value.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// failed to connect
|
||||
Serial.println("failed to connect");
|
||||
}
|
||||
|
||||
NimBLEDevice::deleteClient(pClient);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/** NimBLE_Secure_Server Demo:
|
||||
*
|
||||
* This example demonstrates the secure passkey protected conenction and communication between an esp32 server and an esp32 client.
|
||||
* Please note that esp32 stores auth info in nvs memory. After a successful connection it is possible that a passkey change will be ineffective.
|
||||
* To avoid this clear the memory of the esp32's between security testings. esptool.py is capable of this, example: esptool.py --port /dev/ttyUSB0 erase_flash.
|
||||
*
|
||||
* Created: on Jan 08 2021
|
||||
* Author: mblasee
|
||||
*/
|
||||
|
||||
#include <NimBLEDevice.h>
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Serial.println("Starting NimBLE Server");
|
||||
NimBLEDevice::init("NimBLE");
|
||||
NimBLEDevice::setPower(ESP_PWR_LVL_P9);
|
||||
|
||||
NimBLEDevice::setSecurityAuth(true, true, true);
|
||||
NimBLEDevice::setSecurityPasskey(123456);
|
||||
NimBLEDevice::setSecurityIOCap(BLE_HS_IO_DISPLAY_ONLY);
|
||||
NimBLEServer *pServer = NimBLEDevice::createServer();
|
||||
NimBLEService *pService = pServer->createService("ABCD");
|
||||
NimBLECharacteristic *pNonSecureCharacteristic = pService->createCharacteristic("1234", NIMBLE_PROPERTY::READ );
|
||||
NimBLECharacteristic *pSecureCharacteristic = pService->createCharacteristic("1235", NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::READ_ENC | NIMBLE_PROPERTY::READ_AUTHEN);
|
||||
|
||||
pService->start();
|
||||
pNonSecureCharacteristic->setValue("Hello Non Secure BLE");
|
||||
pSecureCharacteristic->setValue("Hello Secure BLE");
|
||||
|
||||
NimBLEAdvertising *pAdvertising = NimBLEDevice::getAdvertising();
|
||||
pAdvertising->addServiceUUID("ABCD");
|
||||
pAdvertising->start();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
}
|
||||
256
lib/NimBLE-Arduino/examples/NimBLE_Server/NimBLE_Server.ino
Normal file
256
lib/NimBLE-Arduino/examples/NimBLE_Server/NimBLE_Server.ino
Normal file
@@ -0,0 +1,256 @@
|
||||
|
||||
/** NimBLE_Server Demo:
|
||||
*
|
||||
* Demonstrates many of the available features of the NimBLE server library.
|
||||
*
|
||||
* Created: on March 22 2020
|
||||
* Author: H2zero
|
||||
*
|
||||
*/
|
||||
|
||||
#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) {
|
||||
Serial.println("Client connected");
|
||||
Serial.println("Multi-connect support: start advertising");
|
||||
NimBLEDevice::startAdvertising();
|
||||
};
|
||||
/** Alternative onConnect() method to extract details of the connection.
|
||||
* See: src/ble_gap.h for the details of the ble_gap_conn_desc struct.
|
||||
*/
|
||||
void onConnect(NimBLEServer* pServer, ble_gap_conn_desc* desc) {
|
||||
Serial.print("Client address: ");
|
||||
Serial.println(NimBLEAddress(desc->peer_ota_addr).toString().c_str());
|
||||
/** 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 5x interval time for best results.
|
||||
*/
|
||||
pServer->updateConnParams(desc->conn_handle, 24, 48, 0, 60);
|
||||
};
|
||||
void onDisconnect(NimBLEServer* pServer) {
|
||||
Serial.println("Client disconnected - start advertising");
|
||||
NimBLEDevice::startAdvertising();
|
||||
};
|
||||
void onMTUChange(uint16_t MTU, ble_gap_conn_desc* desc) {
|
||||
Serial.printf("MTU updated: %u for connection ID: %u\n", MTU, desc->conn_handle);
|
||||
};
|
||||
|
||||
/********************* Security handled here **********************
|
||||
****** Note: these are the same return values as defaults ********/
|
||||
uint32_t onPassKeyRequest(){
|
||||
Serial.println("Server Passkey Request");
|
||||
/** This should return a random 6 digit number for security
|
||||
* or make your own static passkey as done here.
|
||||
*/
|
||||
return 123456;
|
||||
};
|
||||
|
||||
bool onConfirmPIN(uint32_t pass_key){
|
||||
Serial.print("The passkey YES/NO number: ");Serial.println(pass_key);
|
||||
/** Return false if passkeys don't match. */
|
||||
return true;
|
||||
};
|
||||
|
||||
void onAuthenticationComplete(ble_gap_conn_desc* desc){
|
||||
/** Check that encryption was successful, if not we disconnect the client */
|
||||
if(!desc->sec_state.encrypted) {
|
||||
NimBLEDevice::getServer()->disconnect(desc->conn_handle);
|
||||
Serial.println("Encrypt connection failed - disconnecting client");
|
||||
return;
|
||||
}
|
||||
Serial.println("Starting BLE work!");
|
||||
};
|
||||
};
|
||||
|
||||
/** Handler class for characteristic actions */
|
||||
class CharacteristicCallbacks: public NimBLECharacteristicCallbacks {
|
||||
void onRead(NimBLECharacteristic* pCharacteristic){
|
||||
Serial.print(pCharacteristic->getUUID().toString().c_str());
|
||||
Serial.print(": onRead(), value: ");
|
||||
Serial.println(pCharacteristic->getValue().c_str());
|
||||
};
|
||||
|
||||
void onWrite(NimBLECharacteristic* pCharacteristic) {
|
||||
Serial.print(pCharacteristic->getUUID().toString().c_str());
|
||||
Serial.print(": onWrite(), value: ");
|
||||
Serial.println(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) {
|
||||
Serial.println("Sending notification to clients");
|
||||
};
|
||||
|
||||
|
||||
/** The status returned in status is defined in NimBLECharacteristic.h.
|
||||
* The value returned in code is the NimBLE host return code.
|
||||
*/
|
||||
void onStatus(NimBLECharacteristic* pCharacteristic, Status status, int code) {
|
||||
String str = ("Notification/Indication status code: ");
|
||||
str += status;
|
||||
str += ", return code: ";
|
||||
str += code;
|
||||
str += ", ";
|
||||
str += NimBLEUtils::returnCodeToString(code);
|
||||
Serial.println(str);
|
||||
};
|
||||
|
||||
void onSubscribe(NimBLECharacteristic* pCharacteristic, ble_gap_conn_desc* desc, uint16_t subValue) {
|
||||
String str = "Client ID: ";
|
||||
str += desc->conn_handle;
|
||||
str += " Address: ";
|
||||
str += std::string(NimBLEAddress(desc->peer_ota_addr)).c_str();
|
||||
if(subValue == 0) {
|
||||
str += " Unsubscribed to ";
|
||||
}else if(subValue == 1) {
|
||||
str += " Subscribed to notfications for ";
|
||||
} else if(subValue == 2) {
|
||||
str += " Subscribed to indications for ";
|
||||
} else if(subValue == 3) {
|
||||
str += " Subscribed to notifications and indications for ";
|
||||
}
|
||||
str += std::string(pCharacteristic->getUUID()).c_str();
|
||||
|
||||
Serial.println(str);
|
||||
};
|
||||
};
|
||||
|
||||
/** Handler class for descriptor actions */
|
||||
class DescriptorCallbacks : public NimBLEDescriptorCallbacks {
|
||||
void onWrite(NimBLEDescriptor* pDescriptor) {
|
||||
std::string dscVal = pDescriptor->getValue();
|
||||
Serial.print("Descriptor witten value:");
|
||||
Serial.println(dscVal.c_str());
|
||||
};
|
||||
|
||||
void onRead(NimBLEDescriptor* pDescriptor) {
|
||||
Serial.print(pDescriptor->getUUID().toString().c_str());
|
||||
Serial.println(" Descriptor read");
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
/** Define callback instances globally to use for multiple Charateristics \ Descriptors */
|
||||
static DescriptorCallbacks dscCallbacks;
|
||||
static CharacteristicCallbacks chrCallbacks;
|
||||
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Serial.println("Starting NimBLE Server");
|
||||
|
||||
/** sets device name */
|
||||
NimBLEDevice::init("NimBLE-Arduino");
|
||||
|
||||
/** Optional: set the transmit power, default is 3db */
|
||||
NimBLEDevice::setPower(ESP_PWR_LVL_P9); /** +9db */
|
||||
|
||||
/** 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);
|
||||
|
||||
/** 2904 descriptors are a special case, when createDescriptor is called with
|
||||
* 0x2904 a NimBLE2904 class is created 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 = (NimBLE2904*)pBeefCharacteristic->createDescriptor("2904");
|
||||
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);
|
||||
|
||||
/** Note a 0x2902 descriptor MUST NOT be created as NimBLE will create one automatically
|
||||
* if notification or indication properties are assigned to a characteristic.
|
||||
*/
|
||||
|
||||
/** 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->setScanResponse(true);
|
||||
pAdvertising->start();
|
||||
|
||||
Serial.println("Advertising Started");
|
||||
}
|
||||
|
||||
|
||||
void loop() {
|
||||
/** Do your thing here, this just spams notifications to all connected clients */
|
||||
if(pServer->getConnectedCount()) {
|
||||
NimBLEService* pSvc = pServer->getServiceByUUID("BAAD");
|
||||
if(pSvc) {
|
||||
NimBLECharacteristic* pChr = pSvc->getCharacteristic("F00D");
|
||||
if(pChr) {
|
||||
pChr->notify(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delay(2000);
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* NimBLE_Server_Whitelist demo
|
||||
*
|
||||
* Created May 17, 2021
|
||||
* Author: h2zero
|
||||
*/
|
||||
|
||||
#include <NimBLEDevice.h>
|
||||
|
||||
NimBLECharacteristic* pCharacteristic = nullptr;
|
||||
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"
|
||||
|
||||
|
||||
class MyServerCallbacks: public NimBLEServerCallbacks {
|
||||
void onConnect(NimBLEServer* pServer) {
|
||||
deviceConnected = true;
|
||||
};
|
||||
|
||||
void onDisconnect(NimBLEServer* pServer, ble_gap_conn_desc* desc) {
|
||||
// Peer disconnected, add them to the whitelist
|
||||
// This allows us to use the whitelist to filter connection attempts
|
||||
// which will minimize reconnection time.
|
||||
NimBLEDevice::whiteListAdd(NimBLEAddress(desc->peer_ota_addr));
|
||||
deviceConnected = false;
|
||||
}
|
||||
};
|
||||
|
||||
void onAdvComplete(NimBLEAdvertising *pAdvertising) {
|
||||
Serial.println("Advertising stopped");
|
||||
if (deviceConnected) {
|
||||
return;
|
||||
}
|
||||
// If advertising timed out without connection start advertising without whitelist filter
|
||||
pAdvertising->setScanFilter(false,false);
|
||||
pAdvertising->start();
|
||||
}
|
||||
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
// Create the BLE Device
|
||||
NimBLEDevice::init("ESP32");
|
||||
|
||||
// Create the BLE Server
|
||||
NimBLEServer* pServer = NimBLEDevice::createServer();
|
||||
pServer->setCallbacks(new MyServerCallbacks());
|
||||
pServer->advertiseOnDisconnect(false);
|
||||
|
||||
// Create the BLE Service
|
||||
NimBLEService *pService = pServer->createService(SERVICE_UUID);
|
||||
|
||||
// Create a BLE Characteristic
|
||||
pCharacteristic = pService->createCharacteristic(
|
||||
CHARACTERISTIC_UUID,
|
||||
NIMBLE_PROPERTY::READ |
|
||||
NIMBLE_PROPERTY::WRITE |
|
||||
NIMBLE_PROPERTY::NOTIFY );
|
||||
|
||||
// Start the service
|
||||
pService->start();
|
||||
|
||||
// Start advertising
|
||||
NimBLEAdvertising *pAdvertising = NimBLEDevice::getAdvertising();
|
||||
pAdvertising->addServiceUUID(SERVICE_UUID);
|
||||
pAdvertising->setScanResponse(false);
|
||||
pAdvertising->start();
|
||||
Serial.println("Waiting a client connection to notify...");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// notify changed value
|
||||
if (deviceConnected) {
|
||||
pCharacteristic->setValue((uint8_t*)&value, 4);
|
||||
pCharacteristic->notify();
|
||||
value++;
|
||||
}
|
||||
// disconnecting
|
||||
if (!deviceConnected && oldDeviceConnected) {
|
||||
NimBLEAdvertising *pAdvertising = NimBLEDevice::getAdvertising();
|
||||
if (NimBLEDevice::getWhiteListCount() > 0) {
|
||||
// Allow anyone to scan but only whitelisted can connect.
|
||||
pAdvertising->setScanFilter(false,true);
|
||||
}
|
||||
// advertise with whitelist for 30 seconds
|
||||
pAdvertising->start(30, onAdvComplete);
|
||||
Serial.println("start advertising");
|
||||
oldDeviceConnected = deviceConnected;
|
||||
}
|
||||
// connecting
|
||||
if (deviceConnected && !oldDeviceConnected) {
|
||||
// do stuff here on connecting
|
||||
oldDeviceConnected = deviceConnected;
|
||||
}
|
||||
|
||||
delay(2000);
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/** NimBLE_Service_Data_Advertiser Demo:
|
||||
*
|
||||
* Simple demo of advertising service data that changes every 5 seconds
|
||||
*
|
||||
* Created: on February 7 2021
|
||||
* Author: H2zero
|
||||
*
|
||||
*/
|
||||
|
||||
#include <NimBLEDevice.h>
|
||||
|
||||
#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
|
||||
|
||||
static NimBLEUUID dataUuid(SERVICE_UUID);
|
||||
static NimBLEAdvertising *pAdvertising = NimBLEDevice::getAdvertising();
|
||||
static uint32_t count = 0;
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Serial.println("Starting BLE work!");
|
||||
|
||||
NimBLEDevice::init("svc data");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
pAdvertising->stop();
|
||||
pAdvertising->setServiceData(dataUuid, std::string((char*)&count, sizeof(count)));
|
||||
pAdvertising->start();
|
||||
|
||||
Serial.printf("Advertising count = %d\n", count);
|
||||
count++;
|
||||
delay(5000);
|
||||
}
|
||||
@@ -0,0 +1,194 @@
|
||||
/**
|
||||
* 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"
|
||||
|
||||
// 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 boolean doConnect = false;
|
||||
static boolean connected = false;
|
||||
static boolean doScan = false;
|
||||
static BLERemoteCharacteristic* pRemoteCharacteristic;
|
||||
static BLEAdvertisedDevice* myDevice;
|
||||
|
||||
static void notifyCallback(
|
||||
BLERemoteCharacteristic* pBLERemoteCharacteristic,
|
||||
uint8_t* pData,
|
||||
size_t length,
|
||||
bool isNotify) {
|
||||
Serial.print("Notify callback for characteristic ");
|
||||
Serial.print(pBLERemoteCharacteristic->getUUID().toString().c_str());
|
||||
Serial.print(" of data length ");
|
||||
Serial.println(length);
|
||||
Serial.print("data: ");
|
||||
Serial.println((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) {
|
||||
}
|
||||
|
||||
void onDisconnect(BLEClient* pclient) {
|
||||
connected = false;
|
||||
Serial.println("onDisconnect");
|
||||
}
|
||||
/***************** New - Security handled here ********************
|
||||
****** Note: these are the same return values as defaults ********/
|
||||
uint32_t onPassKeyRequest(){
|
||||
Serial.println("Client PassKeyRequest");
|
||||
return 123456;
|
||||
}
|
||||
bool onConfirmPIN(uint32_t pass_key){
|
||||
Serial.print("The passkey YES/NO number: ");Serial.println(pass_key);
|
||||
return true;
|
||||
}
|
||||
|
||||
void onAuthenticationComplete(ble_gap_conn_desc desc){
|
||||
Serial.println("Starting BLE work!");
|
||||
}
|
||||
/*******************************************************************/
|
||||
};
|
||||
|
||||
bool connectToServer() {
|
||||
Serial.print("Forming a connection to ");
|
||||
Serial.println(myDevice->getAddress().toString().c_str());
|
||||
|
||||
BLEClient* pClient = BLEDevice::createClient();
|
||||
Serial.println(" - Created client");
|
||||
|
||||
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)
|
||||
Serial.println(" - Connected to server");
|
||||
|
||||
// Obtain a reference to the service we are after in the remote BLE server.
|
||||
BLERemoteService* pRemoteService = pClient->getService(serviceUUID);
|
||||
if (pRemoteService == nullptr) {
|
||||
Serial.print("Failed to find our service UUID: ");
|
||||
Serial.println(serviceUUID.toString().c_str());
|
||||
pClient->disconnect();
|
||||
return false;
|
||||
}
|
||||
Serial.println(" - Found our service");
|
||||
|
||||
|
||||
// Obtain a reference to the characteristic in the service of the remote BLE server.
|
||||
pRemoteCharacteristic = pRemoteService->getCharacteristic(charUUID);
|
||||
if (pRemoteCharacteristic == nullptr) {
|
||||
Serial.print("Failed to find our characteristic UUID: ");
|
||||
Serial.println(charUUID.toString().c_str());
|
||||
pClient->disconnect();
|
||||
return false;
|
||||
}
|
||||
Serial.println(" - Found our characteristic");
|
||||
|
||||
// Read the value of the characteristic.
|
||||
if(pRemoteCharacteristic->canRead()) {
|
||||
std::string value = pRemoteCharacteristic->readValue();
|
||||
Serial.print("The characteristic value was: ");
|
||||
Serial.println(value.c_str());
|
||||
}
|
||||
|
||||
if(pRemoteCharacteristic->canNotify())
|
||||
pRemoteCharacteristic->registerForNotify(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(BLEAdvertisedDevice* advertisedDevice) {
|
||||
Serial.print("BLE Advertised Device found: ");
|
||||
Serial.println(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
|
||||
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Serial.println("Starting Arduino BLE Client application...");
|
||||
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->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());
|
||||
pBLEScan->setInterval(1349);
|
||||
pBLEScan->setWindow(449);
|
||||
pBLEScan->setActiveScan(true);
|
||||
pBLEScan->start(5, false);
|
||||
} // End of setup.
|
||||
|
||||
|
||||
// This is the Arduino main loop function.
|
||||
void loop() {
|
||||
|
||||
// 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()) {
|
||||
Serial.println("We are now connected to the BLE Server.");
|
||||
} else {
|
||||
Serial.println("We have failed to connect to the server; there is nothin more we will do.");
|
||||
}
|
||||
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) {
|
||||
String newValue = "Time since boot: " + String(millis()/1000);
|
||||
Serial.println("Setting new characteristic value to \"" + newValue + "\"");
|
||||
|
||||
// Set the characteristic's value to be the array of bytes that is actually a string.
|
||||
/*** Note: write / read value now returns true if successful, false otherwise - try again or disconnect ***/
|
||||
pRemoteCharacteristic->writeValue(newValue.c_str(), newValue.length());
|
||||
}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 in arduino
|
||||
}
|
||||
|
||||
delay(1000); // Delay a second between loops.
|
||||
} // End of loop
|
||||
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
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 pcbreflux
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
Create a BLE server that will send periodic iBeacon frames.
|
||||
The design of creating the BLE server is:
|
||||
1. Create a BLE Server
|
||||
2. Create advertising data
|
||||
3. Start advertising.
|
||||
4. wait
|
||||
5. Stop advertising.
|
||||
6. deep sleep
|
||||
|
||||
*/
|
||||
|
||||
|
||||
/** NimBLE differences highlighted in comment blocks **/
|
||||
|
||||
|
||||
#include "sys/time.h"
|
||||
/*******original********
|
||||
#include "BLEDevice.h"
|
||||
#include "BLEUtils.h"
|
||||
#include "BLEBeacon.h"
|
||||
***********************/
|
||||
#include "NimBLEDevice.h"
|
||||
#include "NimBLEBeacon.h"
|
||||
#include "esp_sleep.h"
|
||||
|
||||
#define GPIO_DEEP_SLEEP_DURATION 10 // sleep x seconds and then wake up
|
||||
RTC_DATA_ATTR static time_t last; // remember last boot in RTC Memory
|
||||
RTC_DATA_ATTR static uint32_t bootcount; // remember number of boots in RTC Memory
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
uint8_t temprature_sens_read();
|
||||
//uint8_t g_phyFuns;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
// See the following for generating UUIDs:
|
||||
// https://www.uuidgenerator.net/
|
||||
BLEAdvertising *pAdvertising;
|
||||
struct timeval now;
|
||||
|
||||
#define BEACON_UUID "8ec76ea3-6668-48da-9866-75be8bc86f4d" // UUID 1 128-Bit (may use linux tool uuidgen or random numbers via https://www.uuidgenerator.net/)
|
||||
|
||||
void setBeacon() {
|
||||
|
||||
BLEBeacon oBeacon = BLEBeacon();
|
||||
oBeacon.setManufacturerId(0x4C00); // fake Apple 0x004C LSB (ENDIAN_CHANGE_U16!)
|
||||
oBeacon.setProximityUUID(BLEUUID(BEACON_UUID));
|
||||
oBeacon.setMajor((bootcount & 0xFFFF0000) >> 16);
|
||||
oBeacon.setMinor(bootcount&0xFFFF);
|
||||
BLEAdvertisementData oAdvertisementData = BLEAdvertisementData();
|
||||
BLEAdvertisementData oScanResponseData = BLEAdvertisementData();
|
||||
|
||||
oAdvertisementData.setFlags(0x04); // BR_EDR_NOT_SUPPORTED 0x04
|
||||
|
||||
std::string strServiceData = "";
|
||||
|
||||
strServiceData += (char)26; // Len
|
||||
strServiceData += (char)0xFF; // Type
|
||||
strServiceData += oBeacon.getData();
|
||||
oAdvertisementData.addData(strServiceData);
|
||||
|
||||
pAdvertising->setAdvertisementData(oAdvertisementData);
|
||||
pAdvertising->setScanResponseData(oScanResponseData);
|
||||
/** pAdvertising->setAdvertisementType(ADV_TYPE_NONCONN_IND);
|
||||
* Advertising mode. Can be one of following constants:
|
||||
* - BLE_GAP_CONN_MODE_NON (non-connectable; 3.C.9.3.2).
|
||||
* - BLE_GAP_CONN_MODE_DIR (directed-connectable; 3.C.9.3.3).
|
||||
* - BLE_GAP_CONN_MODE_UND (undirected-connectable; 3.C.9.3.4).
|
||||
*/
|
||||
pAdvertising->setAdvertisementType(BLE_GAP_CONN_MODE_NON);
|
||||
|
||||
}
|
||||
|
||||
void setup() {
|
||||
|
||||
|
||||
Serial.begin(115200);
|
||||
gettimeofday(&now, NULL);
|
||||
|
||||
Serial.printf("start ESP32 %d\n",bootcount++);
|
||||
|
||||
Serial.printf("deep sleep (%lds since last reset, %lds since last boot)\n",now.tv_sec,now.tv_sec-last);
|
||||
|
||||
last = now.tv_sec;
|
||||
|
||||
// Create the BLE Device
|
||||
BLEDevice::init("");
|
||||
|
||||
// Create the BLE Server
|
||||
// BLEServer *pServer = BLEDevice::createServer(); // <-- no longer required to instantiate BLEServer, less flash and ram usage
|
||||
|
||||
pAdvertising = BLEDevice::getAdvertising();
|
||||
|
||||
setBeacon();
|
||||
// Start advertising
|
||||
pAdvertising->start();
|
||||
Serial.println("Advertizing started...");
|
||||
delay(100);
|
||||
pAdvertising->stop();
|
||||
Serial.printf("enter deep sleep\n");
|
||||
esp_deep_sleep(1000000LL * GPIO_DEEP_SLEEP_DURATION);
|
||||
Serial.printf("in deep sleep\n");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
/*
|
||||
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
|
||||
|
||||
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>
|
||||
|
||||
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) {
|
||||
deviceConnected = true;
|
||||
};
|
||||
|
||||
void onDisconnect(BLEServer* pServer) {
|
||||
deviceConnected = false;
|
||||
}
|
||||
/***************** New - Security handled here ********************
|
||||
****** Note: these are the same return values as defaults ********/
|
||||
uint32_t onPassKeyRequest(){
|
||||
Serial.println("Server PassKeyRequest");
|
||||
return 123456;
|
||||
}
|
||||
|
||||
bool onConfirmPIN(uint32_t pass_key){
|
||||
Serial.print("The passkey YES/NO number: ");Serial.println(pass_key);
|
||||
return true;
|
||||
}
|
||||
|
||||
void onAuthenticationComplete(ble_gap_conn_desc desc){
|
||||
Serial.println("Starting BLE work!");
|
||||
}
|
||||
/*******************************************************************/
|
||||
};
|
||||
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
// 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
|
||||
);
|
||||
|
||||
// https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.client_characteristic_configuration.xml
|
||||
// Create a BLE Descriptor
|
||||
/***************************************************
|
||||
NOTE: DO NOT create a 2902 descriptor.
|
||||
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->setScanResponse(false);
|
||||
/** Note, this could be left out as that is the default value */
|
||||
pAdvertising->setMinPreferred(0x0); // set value to 0x00 to not advertise this parameter
|
||||
|
||||
BLEDevice::startAdvertising();
|
||||
Serial.println("Waiting a client connection to notify...");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// notify changed value
|
||||
if (deviceConnected) {
|
||||
pCharacteristic->setValue((uint8_t*)&value, 4);
|
||||
pCharacteristic->notify();
|
||||
value++;
|
||||
delay(3); // bluetooth stack will go into congestion, if too many packets are sent, in 6 hours test i was able to go as low as 3ms
|
||||
}
|
||||
// disconnecting
|
||||
if (!deviceConnected && oldDeviceConnected) {
|
||||
delay(500); // give the bluetooth stack the chance to get things ready
|
||||
pServer->startAdvertising(); // restart advertising
|
||||
Serial.println("start advertising");
|
||||
oldDeviceConnected = deviceConnected;
|
||||
}
|
||||
// connecting
|
||||
if (deviceConnected && !oldDeviceConnected) {
|
||||
// do stuff here on connecting
|
||||
oldDeviceConnected = deviceConnected;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
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
|
||||
*/
|
||||
|
||||
/** NimBLE differences highlighted in comment blocks **/
|
||||
|
||||
/*******original********
|
||||
#include <BLEDevice.h>
|
||||
#include <BLEUtils.h>
|
||||
#include <BLEScan.h>
|
||||
#include <BLEAdvertisedDevice.h>
|
||||
***********************/
|
||||
|
||||
#include <NimBLEDevice.h>
|
||||
|
||||
int scanTime = 5; //In seconds
|
||||
BLEScan* pBLEScan;
|
||||
|
||||
class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks {
|
||||
/*** Only a reference to the advertised device is passed now
|
||||
void onResult(BLEAdvertisedDevice advertisedDevice) { **/
|
||||
void onResult(BLEAdvertisedDevice* advertisedDevice) {
|
||||
/** Serial.printf("Advertised Device: %s \n", advertisedDevice.toString().c_str()); **/
|
||||
Serial.printf("Advertised Device: %s \n", advertisedDevice->toString().c_str());
|
||||
}
|
||||
};
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Serial.println("Scanning...");
|
||||
|
||||
BLEDevice::init("");
|
||||
pBLEScan = BLEDevice::getScan(); //create new scan
|
||||
pBLEScan->setAdvertisedDeviceCallbacks(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
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// put your main code here, to run repeatedly:
|
||||
BLEScanResults foundDevices = pBLEScan->start(scanTime, false);
|
||||
Serial.print("Devices found: ");
|
||||
Serial.println(foundDevices.getCount());
|
||||
Serial.println("Scan done!");
|
||||
pBLEScan->clearResults(); // delete results fromBLEScan buffer to release memory
|
||||
delay(2000);
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
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
|
||||
*/
|
||||
|
||||
/** NimBLE differences highlighted in comment blocks **/
|
||||
|
||||
/*******original********
|
||||
#include <BLEDevice.h>
|
||||
#include <BLEUtils.h>
|
||||
#include <BLEServer.h>
|
||||
***********************/
|
||||
|
||||
#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"
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Serial.println("Starting BLE work!");
|
||||
|
||||
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->setScanResponse(true);
|
||||
pAdvertising->setMinPreferred(0x06); // functions that help with iPhone connections issue
|
||||
pAdvertising->setMaxPreferred(0x12);
|
||||
|
||||
BLEDevice::startAdvertising();
|
||||
Serial.println("Characteristic defined! Now you can read it in your phone!");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// put your main code here, to run repeatedly:
|
||||
delay(2000);
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
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
|
||||
|
||||
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>
|
||||
|
||||
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) {
|
||||
deviceConnected = true;
|
||||
BLEDevice::startAdvertising();
|
||||
};
|
||||
|
||||
void onDisconnect(BLEServer* pServer) {
|
||||
deviceConnected = false;
|
||||
}
|
||||
/***************** New - Security handled here ********************
|
||||
****** Note: these are the same return values as defaults ********/
|
||||
uint32_t onPassKeyRequest(){
|
||||
Serial.println("Server PassKeyRequest");
|
||||
return 123456;
|
||||
}
|
||||
|
||||
bool onConfirmPIN(uint32_t pass_key){
|
||||
Serial.print("The passkey YES/NO number: ");Serial.println(pass_key);
|
||||
return true;
|
||||
}
|
||||
|
||||
void onAuthenticationComplete(ble_gap_conn_desc desc){
|
||||
Serial.println("Starting BLE work!");
|
||||
}
|
||||
/*******************************************************************/
|
||||
};
|
||||
|
||||
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
// 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
|
||||
);
|
||||
|
||||
// https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.client_characteristic_configuration.xml
|
||||
// Create a BLE Descriptor
|
||||
/***************************************************
|
||||
NOTE: DO NOT create a 2902 descriptor
|
||||
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->setScanResponse(false);
|
||||
/** Note, this could be left out as that is the default value */
|
||||
pAdvertising->setMinPreferred(0x0); // set value to 0x00 to not advertise this parameter
|
||||
|
||||
BLEDevice::startAdvertising();
|
||||
Serial.println("Waiting a client connection to notify...");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// notify changed value
|
||||
if (deviceConnected) {
|
||||
pCharacteristic->setValue((uint8_t*)&value, 4);
|
||||
pCharacteristic->notify();
|
||||
value++;
|
||||
delay(10); // bluetooth stack will go into congestion, if too many packets are sent, in 6 hours test i was able to go as low as 3ms
|
||||
}
|
||||
// disconnecting
|
||||
if (!deviceConnected && oldDeviceConnected) {
|
||||
delay(500); // give the bluetooth stack the chance to get things ready
|
||||
pServer->startAdvertising(); // restart advertising
|
||||
Serial.println("start advertising");
|
||||
oldDeviceConnected = deviceConnected;
|
||||
}
|
||||
// connecting
|
||||
if (deviceConnected && !oldDeviceConnected) {
|
||||
// do stuff here on connecting
|
||||
oldDeviceConnected = deviceConnected;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
/*
|
||||
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
|
||||
|
||||
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>
|
||||
|
||||
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) {
|
||||
deviceConnected = true;
|
||||
};
|
||||
|
||||
void onDisconnect(BLEServer* pServer) {
|
||||
deviceConnected = false;
|
||||
}
|
||||
/***************** New - Security handled here ********************
|
||||
****** Note: these are the same return values as defaults ********/
|
||||
uint32_t onPassKeyRequest(){
|
||||
Serial.println("Server PassKeyRequest");
|
||||
return 123456;
|
||||
}
|
||||
|
||||
bool onConfirmPIN(uint32_t pass_key){
|
||||
Serial.print("The passkey YES/NO number: ");Serial.println(pass_key);
|
||||
return true;
|
||||
}
|
||||
|
||||
void onAuthenticationComplete(ble_gap_conn_desc desc){
|
||||
Serial.println("Starting BLE work!");
|
||||
}
|
||||
/*******************************************************************/
|
||||
};
|
||||
|
||||
class MyCallbacks: public BLECharacteristicCallbacks {
|
||||
void onWrite(BLECharacteristic *pCharacteristic) {
|
||||
std::string rxValue = pCharacteristic->getValue();
|
||||
|
||||
if (rxValue.length() > 0) {
|
||||
Serial.println("*********");
|
||||
Serial.print("Received Value: ");
|
||||
for (int i = 0; i < rxValue.length(); i++)
|
||||
Serial.print(rxValue[i]);
|
||||
|
||||
Serial.println();
|
||||
Serial.println("*********");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
// 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();
|
||||
|
||||
// Start advertising
|
||||
pServer->getAdvertising()->start();
|
||||
Serial.println("Waiting a client connection to notify...");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
|
||||
if (deviceConnected) {
|
||||
pTxCharacteristic->setValue(&txValue, 1);
|
||||
pTxCharacteristic->notify();
|
||||
txValue++;
|
||||
delay(10); // bluetooth stack will go into congestion, if too many packets are sent
|
||||
}
|
||||
|
||||
// disconnecting
|
||||
if (!deviceConnected && oldDeviceConnected) {
|
||||
delay(500); // give the bluetooth stack the chance to get things ready
|
||||
pServer->startAdvertising(); // restart advertising
|
||||
Serial.println("start advertising");
|
||||
oldDeviceConnected = deviceConnected;
|
||||
}
|
||||
// connecting
|
||||
if (deviceConnected && !oldDeviceConnected) {
|
||||
// do stuff here on connecting
|
||||
oldDeviceConnected = deviceConnected;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleWrite.cpp
|
||||
Ported to Arduino ESP32 by Evandro Copercini
|
||||
*/
|
||||
|
||||
/** NimBLE differences highlighted in comment blocks **/
|
||||
|
||||
/*******original********
|
||||
#include <BLEDevice.h>
|
||||
#include <BLEUtils.h>
|
||||
#include <BLEServer.h>
|
||||
***********************/
|
||||
#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"
|
||||
|
||||
|
||||
class MyCallbacks: public BLECharacteristicCallbacks {
|
||||
void onWrite(BLECharacteristic *pCharacteristic) {
|
||||
std::string value = pCharacteristic->getValue();
|
||||
|
||||
if (value.length() > 0) {
|
||||
Serial.println("*********");
|
||||
Serial.print("New value: ");
|
||||
for (int i = 0; i < value.length(); i++)
|
||||
Serial.print(value[i]);
|
||||
|
||||
Serial.println();
|
||||
Serial.println("*********");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
Serial.println("1- Download and install an BLE scanner app in your phone");
|
||||
Serial.println("2- Scan for BLE devices in the app");
|
||||
Serial.println("3- Connect to MyESP32");
|
||||
Serial.println("4- Go to CUSTOM CHARACTERISTIC in CUSTOM SERVICE and write something");
|
||||
Serial.println("5- See the magic =)");
|
||||
|
||||
BLEDevice::init("MyESP32");
|
||||
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->setCallbacks(new MyCallbacks());
|
||||
|
||||
pCharacteristic->setValue("Hello World");
|
||||
pService->start();
|
||||
|
||||
BLEAdvertising *pAdvertising = pServer->getAdvertising();
|
||||
pAdvertising->start();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// put your main code here, to run repeatedly:
|
||||
delay(2000);
|
||||
}
|
||||
Reference in New Issue
Block a user