BT serial: SSP improvements, added missing events (#8723)

* Renamed SerialToSerialBT_SSP_pairing

* Clarified useage of SPP example

* Added missing events

* Implemented dropCache() method to remove bonded devices

* Removed legacy pairing, enhaced SSP

* Minor updates to examples

* Updated README + asociated comments in source and example

* Implemented more methods to manipulate bonded devices + modified example

* Added SPP event

* Reordered event to match enum declaration; added missing events; added comments

* Re-implemented legacy pairing support

* Spelling fixes in README

* Removed unnecessary test in discover()

* Updates

---------

Co-authored-by: Tomas Pilny <tomas.pilny@espressif.com>
Co-authored-by: Tomáš Pilný <pilnyt@seznam.cz>
This commit is contained in:
Me No Dev 2023-10-06 15:49:51 +03:00 committed by GitHub
parent 6603b6ed2f
commit 02e7fd884a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
23 changed files with 773 additions and 409 deletions

View file

@ -1,19 +1,78 @@
### Bluetooth Serial Library
## Bluetooth Serial Library
A simple Serial compatible library using ESP32 classical bluetooth (SPP)
A simple Serial compatible library using ESP32 classical Bluetooth Serial Port Profile (SPP)
Note: Since version 3.0.0 this library does not support legacy pairing (using fixed PIN consisting of 4 digits).
### How to use it?
#### How to use it?
There are 3 basic use cases: phone, other ESP32 or any MCU with a Bluetooth serial module
- Download one bluetooth terminal app in your smartphone<br>
For Android: https://play.google.com/store/apps/details?id=de.kai_morich.serial_bluetooth_terminal <br>
For iOS: https://itunes.apple.com/us/app/hm10-bluetooth-serial-lite/id1030454675
#### Phone
- Download one of the Bluetooth terminal apps to your smartphone
- For [Android](https://play.google.com/store/apps/details?id=de.kai_morich.serial_bluetooth_terminal)
- For [iOS](https://itunes.apple.com/us/app/hm10-bluetooth-serial-lite/id1030454675)
- Flash an example sketch to your ESP32
- Scan and pair the device in your smartphone
- Scan and pair the device to your smartphone
- Open the bluetooth terminal app
- Open the Bluetooth terminal app and connect
- Enjoy
#### ESP32
You can flash one of the ESP32 with the example [`SerialToSerialBTM`](https://github.com/espressif/arduino-esp32/blob/master/libraries/BluetoothSerial/examples/SerialToSerialBTM/SerialToSerialBTM.ino) (the Master) and another ESP32 with [`SerialToSerialBT`](https://github.com/espressif/arduino-esp32/blob/master/libraries/BluetoothSerial/examples/SerialToSerialBT/SerialToSerialBT.ino) (the Slave).
Those examples are preset to work out-of-the-box but they should be scalable to connect multiple Slaves to the Master.
#### 3rd party Serial Bluetooth module
Using a 3rd party Serial Bluetooth module will require to study the documentation of the particular module in order to make it work, however, one side can utilize the mentioned [`SerialToSerialBTM`](https://github.com/espressif/arduino-esp32/blob/master/libraries/BluetoothSerial/examples/SerialToSerialBTM/SerialToSerialBTM.ino) (the Master) or [`SerialToSerialBT`](https://github.com/espressif/arduino-esp32/blob/master/libraries/BluetoothSerial/examples/SerialToSerialBT/SerialToSerialBT.ino) (the Slave).
### Pairing options
There are two easy options and one difficult.
The easy options can be used as usual. These offer pairing with and without Secure Simple Pairing (SSP).
The difficult option offers legacy pairing (using fixed PIN) however this must be compiled with Arduino as an IDF component with disabled sdkconfig option `CONFIG_BT_SSP_ENABLED`.
#### Without SSP
This method will authenticate automatically any attempt to pair and should not be used if security is a concern! This option is used for the examples [`SerialToSerialBTM`](https://github.com/espressif/arduino-esp32/blob/master/libraries/BluetoothSerial/examples/SerialToSerialBTM/SerialToSerialBTM.ino) and [`SerialToSerialBT`](https://github.com/espressif/arduino-esp32/blob/master/libraries/BluetoothSerial/examples/SerialToSerialBT/SerialToSerialBT.ino).
### With SSP
The usage of SSP provides a secure connection. This option is demonstrated in the example `SerialToSerialBT_SSP``](https://github.com/espressif/arduino-esp32/blob/master/libraries/BluetoothSerial/examples/SerialToSerialBT_SSP/SerialToSerialBT_SSP.ino)
The Secure Simple Pairing is enabled by calling method `enableSSP` which has two variants - one is backward compatible without parameter `enableSSP()` and second with parameters `enableSSP(bool inputCapability, bool outputCapability)`. Similarly, the SSP can be disabled by calling `disableSSP()`.
Both options must be called before `begin()` or if it is called after `begin()` the driver needs to be restarted (call `end()` followed by `begin()`) in order to take in effect enabling or disabling the SSP.
#### The parameters define the method of authentication:
**inputCapability** - Defines if ESP32 device has input method (Serial terminal, keyboard or similar)
**outputCapability** - Defines if ESP32 device has output method (Serial terminal, display or similar)
* **inputCapability=true and outputCapability=true**
* Both devices display randomly generated code and if they match the user will authenticate pairing on both devices.
* This must be implemented by registering a callback via `onConfirmRequest()` and in this callback the user will input the response and call `confirmReply(true)` if the authenticated, otherwise call `confirmReply(false)` to reject the pairing.
* **inputCapability=false and outputCapability=false**
* Only the other device authenticates pairing without any pin.
* **inputCapability=false and outputCapability=true**
* Only the other device authenticates pairing without any pin.
* **inputCapability=true and outputCapability=false**
* The user will be required to input the passkey to the ESP32 device to authenticate.
* This must be implemented by registering a callback via `onKeyRequest`()` and in this callback the entered passkey will be responded via `respondPasskey(passkey)`
### Legacy Pairing (IDF component)
To use Legacy pairing you will have to use [Arduino as an IDF component](https://espressif-docs.readthedocs-hosted.com/projects/arduino-esp32/en/latest/esp-idf_component.html) and disable option `CONFIG_BT_SSP_ENABLED`.
Please refer to the documentation on how to setup Arduino as an IDF component and when you are done, run `idf.py menuconfig` navigate to `Component Config -> Bluetooth -> Bluedroid -> [ ] Secure Simple Pairing` and disable it.
While in the menuconfig you will also need to change the partition scheme `Partition Table -> Partition Table -> (X) Single Factory app (large), no OTA`.
After these changes save & quit menuconfig and you are ready to go: `idf.py monitor flash`.
Please note that to use the PIN in smartphones and computers you need to use characters `SerialBT.setPin("1234", 4);` not a number `SerialBT.setPin(1234, 4);` . Numbers CAN be used if the other side uses them too, but phones and computers use characters.

View file

@ -19,19 +19,18 @@
#include <BluetoothSerial.h>
#if !defined(CONFIG_BT_ENABLED) || !defined(CONFIG_BLUEDROID_ENABLED)
#error Bluetooth is not enabled! Please run `make menuconfig` to and enable it
#error Bluetooth is not enabled! Please run `make menuconfig` to and enable it
#endif
#if !defined(CONFIG_BT_SPP_ENABLED)
#error Serial Bluetooth not available or not enabled. It is only available for the ESP32 chip.
#error Serial Bluetooth not available or not enabled. It is only available for the ESP32 chip.
#endif
BluetoothSerial SerialBT;
#define BT_DISCOVER_TIME 10000
esp_spp_sec_t sec_mask=ESP_SPP_SEC_NONE; // or ESP_SPP_SEC_ENCRYPT|ESP_SPP_SEC_AUTHENTICATE to request pincode confirmation
esp_spp_role_t role=ESP_SPP_ROLE_SLAVE; // or ESP_SPP_ROLE_MASTER
esp_spp_sec_t sec_mask = ESP_SPP_SEC_NONE; // or ESP_SPP_SEC_ENCRYPT|ESP_SPP_SEC_AUTHENTICATE to request pincode confirmation
esp_spp_role_t role = ESP_SPP_ROLE_SLAVE; // or ESP_SPP_ROLE_MASTER
// std::map<BTAddress, BTAdvertisedDeviceSet> btDeviceList;

View file

@ -6,11 +6,11 @@
String device_name = "ESP32-example";
#if !defined(CONFIG_BT_ENABLED) || !defined(CONFIG_BLUEDROID_ENABLED)
#error Bluetooth is not enabled! Please run `make menuconfig` to and enable it
#error Bluetooth is not enabled! Please run `make menuconfig` to and enable it
#endif
#if !defined(CONFIG_BT_SPP_ENABLED)
#error Serial Bluetooth not available or not enabled. It is only available for the ESP32 chip.
#error Serial Bluetooth not available or not enabled. It is only available for the ESP32 chip.
#endif
BluetoothSerial SerialBT;

View file

@ -1,22 +1,22 @@
//This example code is in the Public Domain (or CC0 licensed, at your option.)
//By Evandro Copercini - 2018
// This example code is in the Public Domain (or CC0 licensed, at your option.)
// By Evandro Copercini - 2018
//
//This example creates a bridge between Serial and Classical Bluetooth (SPP)
//and also demonstrate that SerialBT have the same functionalities of a normal Serial
// This example creates a bridge between Serial and Classical Bluetooth (SPP)
// and also demonstrate that SerialBT have the same functionalities of a normal Serial
// Note: Pairing is authenticated automatically by this device
#include "BluetoothSerial.h"
//#define USE_PIN // Uncomment this to use PIN during pairing. The pin is specified on the line below
const char *pin = "1234"; // Change this to more secure PIN.
String device_name = "ESP32-BT-Slave";
// Check if Bluetooth is available
#if !defined(CONFIG_BT_ENABLED) || !defined(CONFIG_BLUEDROID_ENABLED)
#error Bluetooth is not enabled! Please run `make menuconfig` to and enable it
#error Bluetooth is not enabled! Please run `make menuconfig` to and enable it
#endif
// Check Serial Port Profile
#if !defined(CONFIG_BT_SPP_ENABLED)
#error Serial Bluetooth not available or not enabled. It is only available for the ESP32 chip.
#error Serial Port Profile for Bluetooth is not available or not enabled. It is only available for the ESP32 chip.
#endif
BluetoothSerial SerialBT;
@ -24,12 +24,8 @@ BluetoothSerial SerialBT;
void setup() {
Serial.begin(115200);
SerialBT.begin(device_name); //Bluetooth device name
//SerialBT.deleteAllBondedDevices(); // Uncomment this to delete paired devices; Must be called after begin
Serial.printf("The device with name \"%s\" is started.\nNow you can pair it with Bluetooth!\n", device_name.c_str());
//Serial.printf("The device with name \"%s\" and MAC address %s is started.\nNow you can pair it with Bluetooth!\n", device_name.c_str(), SerialBT.getMacString()); // Use this after the MAC method is implemented
#ifdef USE_PIN
SerialBT.setPin(pin);
Serial.println("Using PIN");
#endif
}
void loop() {

View file

@ -1,27 +1,33 @@
// This example code is in the Public Domain (or CC0 licensed, at your option.)
// By Victor Tchistiak - 2019
//
// This example demonstrates master mode Bluetooth connection to a slave BT device using PIN (password)
// defined either by String "slaveName" by default "OBDII" or by MAC address
// This example demonstrates master mode Bluetooth connection to a slave BT device
// defined either by String "slaveName" by default "ESP32-BT-Slave" or by MAC address
//
// This example creates a bridge between Serial and Classical Bluetooth (SPP)
// This is an extension of the SerialToSerialBT example by Evandro Copercini - 2018
//
// DO NOT try to connect to phone or laptop - they are master
// devices, same as the ESP using this code - it will NOT work!
// devices, same as the ESP using this code - you will be able
// to pair, but the serial communication will NOT work!
//
// You can try to flash a second ESP32 with the example SerialToSerialBT - it should
// automatically pair with ESP32 running this code
// Note: Pairing is authenticated automatically by this device
#include "BluetoothSerial.h"
#define USE_NAME // Comment this to use MAC address instead of a slaveName
const char *pin = "1234"; // Change this to reflect the pin expected by the real slave BT device
#if !defined(CONFIG_BT_SPP_ENABLED)
#error Serial Bluetooth not available or not enabled. It is only available for the ESP32 chip.
// Check if Bluetooth is available
#if !defined(CONFIG_BT_ENABLED) || !defined(CONFIG_BLUEDROID_ENABLED)
#error Bluetooth is not enabled! Please run `make menuconfig` to and enable it
#endif
// Check Serial Port Profile
#if !defined(CONFIG_BT_SPP_ENABLED)
#error Serial Port Profile for Bluetooth is not available or not enabled. It is only available for the ESP32 chip.
#endif
BluetoothSerial SerialBT;
#ifdef USE_NAME
@ -38,6 +44,7 @@ void setup() {
Serial.begin(115200);
SerialBT.begin(myName, true);
//SerialBT.deleteAllBondedDevices(); // Uncomment this to delete paired devices; Must be called after begin
Serial.printf("The device \"%s\" started in master mode, make sure slave BT device is on!\n", myName.c_str());
#ifndef USE_NAME

View file

@ -0,0 +1,65 @@
// This example code is in the Public Domain (or CC0 licensed, at your option.)
//
// This example creates a bridge between Serial and Classical Bluetooth (SPP with authentication)
// and also demonstrate that SerialBT have the same functionalities of a normal Serial
// Legacy pairing TODO
// Must be run as idf component ... todo
#include "BluetoothSerial.h"
// Check if Bluetooth is available
#if !defined(CONFIG_BT_ENABLED) || !defined(CONFIG_BLUEDROID_ENABLED)
#error Bluetooth is not enabled! Please run `make menuconfig` to and enable it
#endif
// Check Serial Port Profile
#if !defined(CONFIG_BT_SPP_ENABLED)
#error Serial Port Profile for Bluetooth is not available or not enabled. It is only available for the ESP32 chip.
#endif
// Check Simple Secure Pairing
#if defined(CONFIG_BT_SSP_ENABLED)
#warning Legacy Pairing is disabled (CONFIG_BT_SSP_ENABLED is enabled. Disable it in menuconfig).
void setup(){}
void loop(){}
#else
const char * deviceName = "ESP32_Legacy_example";
BluetoothSerial SerialBT;
bool confirmRequestDone = false;
void BTAuthCompleteCallback(boolean success){
if (success){
confirmRequestDone = true;
Serial.println("Pairing success!!");
} else {
Serial.println("Pairing failed, rejected by user!!");
}
}
void serial_response(){
if (Serial.available()){
SerialBT.write(Serial.read());
}
if (SerialBT.available()){
Serial.write(SerialBT.read());
}
delay(20);
}
void setup(){
Serial.begin(115200);
SerialBT.onAuthComplete(BTAuthCompleteCallback);
SerialBT.begin(deviceName); // Initiate Bluetooth device with name in parameter
SerialBT.setPin("1234", 4);
Serial.printf("The device started with name \"%s\", now you can pair it with Bluetooth!\n", deviceName);
}
void loop(){
if (confirmRequestDone){
serial_response();
} else {
delay(1); // Feed the watchdog
}
}
#endif

View file

@ -0,0 +1,134 @@
// This example code is in the Public Domain (or CC0 licensed, at your option.)
// By Richard Li - 2020
//
// This example creates a bridge between Serial and Classical Bluetooth (SPP with authentication)
// and also demonstrate that SerialBT have the same functionalities of a normal Serial
// SSP - Simple Secure Pairing - The device (ESP32) will display random number and the user is responsible of comparing it to the number
// displayed on the other device (for example phone).
// If the numbers match the user authenticates the pairing on both devices - on phone simply press "Pair" and in terminal for the sketch send 'Y' or 'y' to confirm.
// Alternatively uncomment AUTO_PAIR to skip the terminal confirmation.
#include "BluetoothSerial.h"
//#define AUTO_PAIR // Uncomment to automatically authenticate ESP32 side
// Check if Bluetooth is available
#if !defined(CONFIG_BT_ENABLED) || !defined(CONFIG_BLUEDROID_ENABLED)
#error Bluetooth is not enabled! Please run `make menuconfig` to and enable it
#endif
// Check Serial Port Profile
#if !defined(CONFIG_BT_SPP_ENABLED)
#error Serial Port Profile for Bluetooth is not available or not enabled. It is only available for the ESP32 chip.
#endif
// Check Simple Secure Pairing
#if !defined(CONFIG_BT_SSP_ENABLED)
#error Simple Secure Pairing for Bluetooth is not available or not enabled.
#endif
const char * deviceName = "ESP32_SSP_example";
// The following lines defines the method of pairing
// When both Input and Output are false only the other device authenticates pairing without any pin.
// When Output is true and Input is false only the other device authenticates pairing without any pin.
// When both Input and Output are true both devices display randomly generated code and if they match authenticate pairing on both devices
// - This must be implemented by registering callback via onConfirmRequest() and in this callback request user input and call confirmReply(true); if the authenticated
// otherwise call `confirmReply(false)` to reject the pairing.
// When Input is true and Output is false User will be required to input the passkey to the ESP32 device to authenticate.
// - This must be implemented by registering callback via onKeyRequest() and in this callback the entered passkey will be responded via respondPasskey(passkey);
const bool INPUT_CAPABILITY = false; // Defines if ESP32 device has input method (Serial terminal, keyboard or similar)
const bool OUTPUT_CAPABILITY = true; // Defines if ESP32 device has output method (Serial terminal, display or similar)
BluetoothSerial SerialBT;
bool confirmRequestDone = false;
void BTConfirmRequestCallback(uint32_t numVal){
confirmRequestDone = false;
#ifndef AUTO_PAIR
Serial.printf("The PIN is: %06lu. If it matches number displayed on the other device write \'Y\' or \'y\':\n", numVal); // Note the formatting "%06lu" - PIN can start with zero(s) which would be ignored with simple "%lu"
while (!Serial.available()) {
delay(1); // Feed the watchdog
// Wait until data is available on the Serial port.
}
Serial.printf("Oh you sent %d Bytes, lets see...", Serial.available());
int dat = Serial.read();
if (dat == 'Y' || dat == 'y'){
SerialBT.confirmReply(true);
}
else{
SerialBT.confirmReply(false);
}
#else
SerialBT.confirmReply(true);
#endif
}
void BTKeyRequestCallback(){
Serial.println("BTKeyRequestCallback"); // debug
char buffer[7] = {0}; // 6 bytes for number, one for termination '0'
while (1) {
Serial.print("Enter the passkey displayed on the other device: ");
while (!Serial.available()) {
delay(1); // Feed the watchdog
// Wait until data is available on the Serial port.
}
size_t len = Serial.readBytesUntil('\n', buffer, sizeof(buffer) - 1);
buffer[len] = '\0'; // Null-terminate the string.
try {
uint32_t passkey = std::stoi(buffer);
Serial.printf("Entered PIN: %lu\n", passkey);
SerialBT.respondPasskey(passkey);
return;
} catch (...) {
Serial.print("Wrong PIN! Try again.");
} // try
} // while(1)
}
void BTAuthCompleteCallback(boolean success){
if (success){
confirmRequestDone = true;
Serial.println("Pairing success!!");
} else {
Serial.println("Pairing failed, rejected by user!!");
}
}
void serial_response(){
if (Serial.available()){
SerialBT.write(Serial.read());
}
if (SerialBT.available()){
Serial.write(SerialBT.read());
}
delay(20);
}
void setup(){
Serial.begin(115200);
SerialBT.enableSSP(INPUT_CAPABILITY, OUTPUT_CAPABILITY); // Must be called before begin
SerialBT.onConfirmRequest(BTConfirmRequestCallback);
SerialBT.onKeyRequest(BTKeyRequestCallback);
SerialBT.onAuthComplete(BTAuthCompleteCallback);
SerialBT.begin(deviceName); // Initiate Bluetooth device with name in parameter
//SerialBT.deleteAllBondedDevices(); // Uncomment this to delete paired devices; Must be called after begin
Serial.printf("The device started with name \"%s\", now you can pair it with Bluetooth!\n", deviceName);
if(INPUT_CAPABILITY and OUTPUT_CAPABILITY){
Serial.println("Both devices will display randomly generated code and if they match authenticate pairing on both devices");
}else if(not INPUT_CAPABILITY and not OUTPUT_CAPABILITY){
Serial.println("Authenticate pairing on the other device. No PIN is used");
}else if(not INPUT_CAPABILITY and OUTPUT_CAPABILITY){
Serial.println("Authenticate pairing on the other device. No PIN is used");
}else if(INPUT_CAPABILITY and not OUTPUT_CAPABILITY){
Serial.println("After pairing is initiated you will be required to enter the passkey to the ESP32 device to authenticate\n > The Passkey will displayed on the other device");
}
}
void loop(){
if (confirmRequestDone){
serial_response();
} else {
delay(1); // Feed the watchdog
}
}

View file

@ -1,79 +0,0 @@
//This example code is in the Public Domain (or CC0 licensed, at your option.)
//By Richard Li - 2020
//
//This example creates a bridge between Serial and Classical Bluetooth (SPP with authentication)
//and also demonstrate that SerialBT have the same functionalities of a normal Serial
#include "BluetoothSerial.h"
#if !defined(CONFIG_BT_ENABLED) || !defined(CONFIG_BLUEDROID_ENABLED)
#error Bluetooth is not enabled! Please run `make menuconfig` to and enable it
#endif
#if !defined(CONFIG_BT_SPP_ENABLED)
#error Serial Bluetooth not available or not enabled. It is only available for the ESP32 chip.
#endif
BluetoothSerial SerialBT;
boolean confirmRequestPending = true;
void BTConfirmRequestCallback(uint32_t numVal)
{
confirmRequestPending = true;
Serial.println(numVal);
}
void BTAuthCompleteCallback(boolean success)
{
confirmRequestPending = false;
if (success)
{
Serial.println("Pairing success!!");
}
else
{
Serial.println("Pairing failed, rejected by user!!");
}
}
void setup()
{
Serial.begin(115200);
SerialBT.enableSSP();
SerialBT.onConfirmRequest(BTConfirmRequestCallback);
SerialBT.onAuthComplete(BTAuthCompleteCallback);
SerialBT.begin("ESP32test"); //Bluetooth device name
Serial.println("The device started, now you can pair it with bluetooth!");
}
void loop()
{
if (confirmRequestPending)
{
if (Serial.available())
{
int dat = Serial.read();
if (dat == 'Y' || dat == 'y')
{
SerialBT.confirmReply(true);
}
else
{
SerialBT.confirmReply(false);
}
}
}
else
{
if (Serial.available())
{
SerialBT.write(Serial.read());
}
if (SerialBT.available())
{
Serial.write(SerialBT.read());
}
delay(20);
}
}

View file

@ -1,17 +1,17 @@
#include <BluetoothSerial.h>
#if !defined(CONFIG_BT_ENABLED) || !defined(CONFIG_BLUEDROID_ENABLED)
#error Bluetooth is not enabled! Please run `make menuconfig` to and enable it
#error Bluetooth is not enabled! Please run `make menuconfig` to and enable it
#endif
#if !defined(CONFIG_BT_SPP_ENABLED)
#error Serial Bluetooth not available or not enabled. It is only available for the ESP32 chip.
#error Serial Bluetooth not available or not enabled. It is only available for the ESP32 chip.
#endif
BluetoothSerial SerialBT;
#define BT_DISCOVER_TIME 10000
#define BT_DISCOVER_TIME 10000
static bool btScanAsync = true;
@ -19,7 +19,7 @@ static bool btScanSync = true;
void btAdvertisedDeviceFound(BTAdvertisedDevice* pDevice) {
Serial.printf("Found a device asynchronously: %s\n", pDevice->toString().c_str());
Serial.printf("Found a device asynchronously: %s\n", pDevice->toString().c_str());
}
void setup() {
@ -29,7 +29,7 @@ void setup() {
if (btScanAsync) {
Serial.print("Starting discoverAsync...");
Serial.print("Starting asynchronous discovery... ");
if (SerialBT.discoverAsync(btAdvertisedDeviceFound)) {
Serial.println("Findings will be reported in \"btAdvertisedDeviceFound\"");
delay(10000);
@ -37,12 +37,12 @@ void setup() {
SerialBT.discoverAsyncStop();
Serial.println("stopped");
} else {
Serial.println("Error on discoverAsync f.e. not workin after a \"connect\"");
Serial.println("Error on discoverAsync f.e. not working after a \"connect\"");
}
}
if (btScanSync) {
Serial.println("Starting discover...");
Serial.println("Starting synchronous discovery... ");
BTScanResults *pResults = SerialBT.discover(BT_DISCOVER_TIME);
if (pResults)
pResults->dump(&Serial);

View file

@ -1,91 +1,74 @@
//This example code is in the Public Domain (or CC0 licensed, at your option.)
//By Victor Tchistiak - 2019
// This example code is in the Public Domain (or CC0 licensed, at your option.)
// Originally by Victor Tchistiak - 2019
// Rewritten with new API by Tomas Pilny - 2023
//
//This example demonstrates reading and removing paired devices stored on the ESP32 flash memory
//Sometimes you may find your ESP32 device could not connect to the remote device despite
//many successful connections earlier. This is most likely a result of client replacing your paired
//device info with new one from other device. The BT clients store connection info for paired devices,
//but it is limited to a few devices only. When new device pairs and number of stored devices is exceeded,
//one of the previously paired devices would be replaced with new one.
//The only remedy is to delete this saved bound device from your device flash memory
//and pair with the other device again.
//
#include "esp_bt_main.h"
#include "esp_bt_device.h"
#include"esp_gap_bt_api.h"
#include "esp_err.h"
// This example demonstrates reading and removing paired devices stored on the ESP32 flash memory
// Sometimes you may find your ESP32 device could not connect to the remote device despite
// many successful connections earlier. This is most likely a result of client replacing your paired
// device info with new one from other device. The BT clients store connection info for paired devices,
// but it is limited to a few devices only. When new device pairs and number of stored devices is exceeded,
// one of the previously paired devices would be replaced with new one.
// The only remedy is to delete this saved bound device from your device flash memory
// and pair with the other device again.
#include "BluetoothSerial.h"
//#include "esp_bt_device.h"
#if !defined(CONFIG_BT_SPP_ENABLED)
#error Serial Bluetooth not available or not enabled. It is only available for the ESP32 chip.
#error Serial Bluetooth not available or not enabled. It is only available for the ESP32 chip.
#endif
#define REMOVE_BONDED_DEVICES 0 // <- Set to 0 to view all bonded devices addresses, set to 1 to remove
#define REMOVE_BONDED_DEVICES true // <- Set to `false` to view all bonded devices addresses, set to `true` to remove
#define PAIR_MAX_DEVICES 20
uint8_t pairedDeviceBtAddr[PAIR_MAX_DEVICES][6];
char bda_str[18];
BluetoothSerial SerialBT;
bool initBluetooth()
{
if(!btStart()) {
Serial.println("Failed to initialize controller");
return false;
}
if(esp_bluedroid_init() != ESP_OK) {
Serial.println("Failed to initialize bluedroid");
return false;
}
if(esp_bluedroid_enable() != ESP_OK) {
Serial.println("Failed to enable bluedroid");
return false;
}
return true;
}
char *bda2str(const uint8_t* bda, char *str, size_t size)
{
if (bda == NULL || str == NULL || size < 18) {
char *bda2str(const uint8_t* bda, char *str, size_t size){
if (bda == NULL || str == NULL || size < 18){
return NULL;
}
sprintf(str, "%02x:%02x:%02x:%02x:%02x:%02x",
bda[0], bda[1], bda[2], bda[3], bda[4], bda[5]);
return str;
}
void setup() {
void setup(){
char bda_str[18];
uint8_t pairedDeviceBtAddr[PAIR_MAX_DEVICES][6];
Serial.begin(115200);
initBluetooth();
Serial.print("ESP32 bluetooth address: "); Serial.println(bda2str(esp_bt_dev_get_address(), bda_str, 18));
SerialBT.begin();
Serial.printf("ESP32 bluetooth address: %s\n", SerialBT.getBtAddressString().c_str());
// SerialBT.deleteAllBondedDevices(); // If you want just delete all, this is the way
// Get the numbers of bonded/paired devices in the BT module
int count = esp_bt_gap_get_bond_device_num();
if(!count) {
Serial.println("No bonded device found.");
int count = SerialBT.getNumberOfBondedDevices();
if(!count){
Serial.println("No bonded devices found.");
} else {
Serial.print("Bonded device count: "); Serial.println(count);
if(PAIR_MAX_DEVICES < count) {
count = PAIR_MAX_DEVICES;
Serial.print("Reset bonded device count: "); Serial.println(count);
Serial.printf("Bonded device count: %d\n", count);
if(PAIR_MAX_DEVICES < count){
count = PAIR_MAX_DEVICES;
Serial.printf("Reset %d bonded devices\n", count);
}
esp_err_t tError = esp_bt_gap_get_bond_device_list(&count, pairedDeviceBtAddr);
if(ESP_OK == tError) {
for(int i = 0; i < count; i++) {
Serial.print("Found bonded device # "); Serial.print(i); Serial.print(" -> ");
Serial.println(bda2str(pairedDeviceBtAddr[i], bda_str, 18));
if(REMOVE_BONDED_DEVICES) {
esp_err_t tError = esp_bt_gap_remove_bond_device(pairedDeviceBtAddr[i]);
if(ESP_OK == tError) {
Serial.print("Removed bonded device # ");
} else {
Serial.print("Failed to remove bonded device # ");
}
Serial.println(i);
count = SerialBT.getBondedDevices(count, pairedDeviceBtAddr);
char rmt_name[ESP_BT_GAP_MAX_BDNAME_LEN + 1];
if(count > 0){
for(int i = 0; i < count; i++){
SerialBT.requestRemoteName(pairedDeviceBtAddr[i]);
while(!SerialBT.readRemoteName(rmt_name)){
delay(1); // Wait for response with the device name
}
}
}
}
Serial.printf("Found bonded device #%d BDA:%s; Name:\"%s\"\n", i, bda2str(pairedDeviceBtAddr[i], bda_str, 18), rmt_name);
SerialBT.invalidateRemoteName(); // Allows waiting for next reading
if(REMOVE_BONDED_DEVICES){
if(SerialBT.deleteBondedDevice(pairedDeviceBtAddr[i])){
Serial.printf("Removed bonded device # %d\n", i);
} else {
Serial.printf("Failed to remove bonded device # %d", i);
} // if(ESP_OK == tError)
} // if(REMOVE_BONDED_DEVICES)
} // for(int i = 0; i < count; i++)
} // if(ESP_OK == tError)
} // if(!count)
}
void loop() {}

View file

@ -20,21 +20,21 @@ class BTAdvertisedDeviceSet;
class BTScanResults {
public:
virtual ~BTScanResults() = default;
virtual ~BTScanResults() = default;
virtual void dump(Print *print = nullptr);
virtual int getCount();
virtual void dump(Print *print = nullptr);
virtual int getCount();
virtual BTAdvertisedDevice* getDevice(int i);
};
class BTScanResultsSet : public BTScanResults {
public:
void dump(Print *print = nullptr);
int getCount();
BTAdvertisedDevice* getDevice(int i);
void dump(Print *print = nullptr);
int getCount();
BTAdvertisedDevice* getDevice(int i);
bool add(BTAdvertisedDeviceSet advertisedDevice, bool unique = true);
void clear();
bool add(BTAdvertisedDeviceSet advertisedDevice, bool unique = true);
void clear();
std::map<std::string, BTAdvertisedDeviceSet> m_vectorAdvertisedDevices;
};

View file

@ -24,10 +24,11 @@
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BLUEDROID_ENABLED)
#ifdef ARDUINO_ARCH_ESP32
#include "esp32-hal-log.h"
#include "esp32-hal-log.h"
#endif
#include "BluetoothSerial.h"
#include "BTAdvertisedDevice.h"
#include "esp_bt.h"
#include "esp_bt_main.h"
@ -58,7 +59,10 @@ static esp_spp_cb_t * custom_spp_callback = NULL;
static BluetoothSerialDataCb custom_data_callback = NULL;
static esp_bd_addr_t current_bd_addr;
static ConfirmRequestCb confirm_request_callback = NULL;
static KeyRequestCb key_request_callback = NULL;
static AuthCompleteCb auth_complete_callback = NULL;
static bool _rmt_name_valid = false;
static uint8_t _rmt_name[ESP_BT_GAP_MAX_BDNAME_LEN + 1] = {0};
#define INQ_LEN 0x10
#define INQ_NUM_RSPS 20
@ -68,10 +72,13 @@ static esp_bd_addr_t _peer_bd_addr;
static char _remote_name[ESP_BT_GAP_MAX_BDNAME_LEN + 1];
static bool _isRemoteAddressSet;
static bool _isMaster;
static esp_bt_pin_code_t _pin_code;
static int _pin_len;
static bool _isPinSet;
static bool _enableSSP;
#ifdef CONFIG_BT_SSP_ENABLED
static bool _enableSSP;
static bool _IO_CAP_INPUT;
static bool _IO_CAP_OUTPUT;
#endif
esp_bt_pin_code_t _pin_code = {0};
uint8_t _pin_code_len = 0; // Number of valid Bytes in the esp_bt_pin_code_t array
static esp_spp_sec_t _sec_mask;
static esp_spp_role_t _role;
// start connect on ESP_SPP_DISCOVERY_COMP_EVT or save entry for getChannels
@ -139,22 +146,6 @@ static bool get_name_from_eir(uint8_t *eir, char *bdname, uint8_t *bdname_len)
return false;
}
static bool btSetPin() {
esp_bt_pin_type_t pin_type;
if (_isPinSet) {
if (_pin_len) {
log_i("pin set");
pin_type = ESP_BT_PIN_TYPE_FIXED;
} else {
_isPinSet = false;
log_i("pin reset");
pin_type = ESP_BT_PIN_TYPE_VARIABLE; // pin_code would be ignored (default)
}
return (esp_bt_gap_set_pin(pin_type, _pin_len, _pin_code) == ESP_OK);
}
return false;
}
static esp_err_t _spp_queue_packet(uint8_t *data, size_t len){
if(!data || !len){
log_w("No data provided");
@ -259,7 +250,7 @@ static void esp_spp_cb(esp_spp_cb_event_t event, esp_spp_cb_param_t *param)
{
switch (event)
{
case ESP_SPP_INIT_EVT:
case ESP_SPP_INIT_EVT: // Enum 0 - When SPP is initialized
log_i("ESP_SPP_INIT_EVT");
#ifdef ESP_IDF_VERSION_MAJOR
esp_bt_gap_set_scan_mode(ESP_BT_CONNECTABLE, ESP_BT_GENERAL_DISCOVERABLE);
@ -273,80 +264,11 @@ static void esp_spp_cb(esp_spp_cb_event_t event, esp_spp_cb_param_t *param)
xEventGroupSetBits(_spp_event_group, SPP_RUNNING);
break;
case ESP_SPP_SRV_OPEN_EVT://Server connection open
if (param->srv_open.status == ESP_SPP_SUCCESS) {
log_i("ESP_SPP_SRV_OPEN_EVT: %u", _spp_client);
if (!_spp_client){
_spp_client = param->srv_open.handle;
_spp_tx_buffer_len = 0;
} else {
secondConnectionAttempt = true;
esp_spp_disconnect(param->srv_open.handle);
}
xEventGroupClearBits(_spp_event_group, SPP_DISCONNECTED);
xEventGroupSetBits(_spp_event_group, SPP_CONNECTED);
} else {
log_e("ESP_SPP_SRV_OPEN_EVT Failed!, status:%d", param->srv_open.status);
}
case ESP_SPP_UNINIT_EVT: // Enum 1 - When SPP is deinitialized
log_i("ESP_SPP_UNINIT_EVT: SPP is deinitialized");
break;
case ESP_SPP_CLOSE_EVT://Client connection closed
if ((param->close.async == false && param->close.status == ESP_SPP_SUCCESS) || param->close.async) {
log_i("ESP_SPP_CLOSE_EVT status:%d handle:%d close_by_remote:%d attempt %u", param->close.status,
param->close.handle, param->close.async, secondConnectionAttempt);
if(secondConnectionAttempt) {
secondConnectionAttempt = false;
} else {
_spp_client = 0;
xEventGroupSetBits(_spp_event_group, SPP_DISCONNECTED);
xEventGroupSetBits(_spp_event_group, SPP_CONGESTED);
xEventGroupSetBits(_spp_event_group, SPP_CLOSED);
xEventGroupClearBits(_spp_event_group, SPP_CONNECTED);
}
} else {
log_e("ESP_SPP_CLOSE_EVT failed!, status:%d", param->close.status);
}
break;
case ESP_SPP_CONG_EVT://connection congestion status changed
if(param->cong.cong){
xEventGroupClearBits(_spp_event_group, SPP_CONGESTED);
} else {
xEventGroupSetBits(_spp_event_group, SPP_CONGESTED);
}
log_v("ESP_SPP_CONG_EVT: %s", param->cong.cong?"CONGESTED":"FREE");
break;
case ESP_SPP_WRITE_EVT://write operation completed
if (param->write.status == ESP_SPP_SUCCESS) {
if(param->write.cong){
xEventGroupClearBits(_spp_event_group, SPP_CONGESTED);
}
log_v("ESP_SPP_WRITE_EVT: %u %s", param->write.len, param->write.cong?"CONGESTED":"");
} else {
log_e("ESP_SPP_WRITE_EVT failed!, status:%d", param->write.status);
}
xSemaphoreGive(_spp_tx_done);//we can try to send another packet
break;
case ESP_SPP_DATA_IND_EVT://connection received data
log_v("ESP_SPP_DATA_IND_EVT len=%d handle=%d", param->data_ind.len, param->data_ind.handle);
//esp_log_buffer_hex("",param->data_ind.data,param->data_ind.len); //for low level debug
//ets_printf("r:%u\n", param->data_ind.len);
if(custom_data_callback){
custom_data_callback(param->data_ind.data, param->data_ind.len);
} else if (_spp_rx_queue != NULL){
for (int i = 0; i < param->data_ind.len; i++){
if(xQueueSend(_spp_rx_queue, param->data_ind.data + i, (TickType_t)0) != pdTRUE){
log_e("RX Full! Discarding %u bytes", param->data_ind.len - i);
break;
}
}
}
break;
case ESP_SPP_DISCOVERY_COMP_EVT://discovery complete
case ESP_SPP_DISCOVERY_COMP_EVT: // Enum 8 - When SDP discovery complete
log_i("ESP_SPP_DISCOVERY_COMP_EVT num=%d", param->disc_comp.scn_num);
if (param->disc_comp.status == ESP_SPP_SUCCESS) {
for(int i=0; i < param->disc_comp.scn_num; i++) {
@ -380,7 +302,7 @@ static void esp_spp_cb(esp_spp_cb_event_t event, esp_spp_cb_param_t *param)
xEventGroupSetBits(_bt_event_group, BT_SDP_COMPLETED);
break;
case ESP_SPP_OPEN_EVT://Client connection open
case ESP_SPP_OPEN_EVT: // Enum 26 - When SPP Client connection open
log_i("ESP_SPP_OPEN_EVT");
if (!_spp_client){
_spp_client = param->open.handle;
@ -393,11 +315,29 @@ static void esp_spp_cb(esp_spp_cb_event_t event, esp_spp_cb_param_t *param)
xEventGroupSetBits(_spp_event_group, SPP_CONGESTED);
break;
case ESP_SPP_START_EVT://server started
case ESP_SPP_CLOSE_EVT: // Enum 27 - When SPP connection closed
if ((param->close.async == false && param->close.status == ESP_SPP_SUCCESS) || param->close.async) {
log_i("ESP_SPP_CLOSE_EVT status:%d handle:%d close_by_remote:%d attempt %u", param->close.status,
param->close.handle, param->close.async, secondConnectionAttempt);
if(secondConnectionAttempt) {
secondConnectionAttempt = false;
} else {
_spp_client = 0;
xEventGroupSetBits(_spp_event_group, SPP_DISCONNECTED);
xEventGroupSetBits(_spp_event_group, SPP_CONGESTED);
xEventGroupSetBits(_spp_event_group, SPP_CLOSED);
xEventGroupClearBits(_spp_event_group, SPP_CONNECTED);
}
} else {
log_e("ESP_SPP_CLOSE_EVT failed!, status:%d", param->close.status);
}
break;
case ESP_SPP_START_EVT: // Enum 28 - When SPP server started
log_i("ESP_SPP_START_EVT");
break;
case ESP_SPP_CL_INIT_EVT://client initiated a connection
case ESP_SPP_CL_INIT_EVT: // Enum 29 - When SPP client initiated a connection
if (param->cl_init.status == ESP_SPP_SUCCESS) {
log_i("ESP_SPP_CL_INIT_EVT handle:%d sec_id:%d", param->cl_init.handle, param->cl_init.sec_id);
} else {
@ -405,8 +345,75 @@ static void esp_spp_cb(esp_spp_cb_event_t event, esp_spp_cb_param_t *param)
}
break;
case ESP_SPP_DATA_IND_EVT: // Enum 30 - When SPP connection received data, only for ESP_SPP_MODE_CB
log_v("ESP_SPP_DATA_IND_EVT len=%d handle=%d", param->data_ind.len, param->data_ind.handle);
//esp_log_buffer_hex("",param->data_ind.data,param->data_ind.len); //for low level debug
//ets_printf("r:%u\n", param->data_ind.len);
if(custom_data_callback){
custom_data_callback(param->data_ind.data, param->data_ind.len);
} else if (_spp_rx_queue != NULL){
for (int i = 0; i < param->data_ind.len; i++){
if(xQueueSend(_spp_rx_queue, param->data_ind.data + i, (TickType_t)0) != pdTRUE){
log_e("RX Full! Discarding %u bytes", param->data_ind.len - i);
break;
}
}
}
break;
case ESP_SPP_CONG_EVT: // Enum 31 - When SPP connection congestion status changed, only for ESP_SPP_MODE_CB
if(param->cong.cong){
xEventGroupClearBits(_spp_event_group, SPP_CONGESTED);
} else {
xEventGroupSetBits(_spp_event_group, SPP_CONGESTED);
}
log_v("ESP_SPP_CONG_EVT: %s", param->cong.cong?"CONGESTED":"FREE");
break;
case ESP_SPP_WRITE_EVT: // Enum 33 - When SPP write operation completes, only for ESP_SPP_MODE_CB
if (param->write.status == ESP_SPP_SUCCESS) {
if(param->write.cong){
xEventGroupClearBits(_spp_event_group, SPP_CONGESTED);
}
log_v("ESP_SPP_WRITE_EVT: %u %s", param->write.len, param->write.cong?"CONGESTED":"");
} else {
log_e("ESP_SPP_WRITE_EVT failed!, status:%d", param->write.status);
}
xSemaphoreGive(_spp_tx_done);//we can try to send another packet
break;
case ESP_SPP_SRV_OPEN_EVT: // Enum 34 - When SPP Server connection open
if (param->srv_open.status == ESP_SPP_SUCCESS) {
log_i("ESP_SPP_SRV_OPEN_EVT: %u", _spp_client);
if (!_spp_client){
_spp_client = param->srv_open.handle;
_spp_tx_buffer_len = 0;
} else {
secondConnectionAttempt = true;
esp_spp_disconnect(param->srv_open.handle);
}
xEventGroupClearBits(_spp_event_group, SPP_DISCONNECTED);
xEventGroupSetBits(_spp_event_group, SPP_CONNECTED);
} else {
log_e("ESP_SPP_SRV_OPEN_EVT Failed!, status:%d", param->srv_open.status);
}
break;
case ESP_SPP_SRV_STOP_EVT: // Enum 35 - When SPP server stopped
log_i("ESP_SPP_SRV_STOP_EVT");
break;
case ESP_SPP_VFS_REGISTER_EVT: // Enum 36 - When SPP VFS register
log_i("ESP_SPP_VFS_REGISTER_EVT");
break;
case ESP_SPP_VFS_UNREGISTER_EVT: // Enum 37 - When SPP VFS unregister
log_i("ESP_SPP_VFS_UNREGISTER_EVT");
break;
default:
log_i("ESP_SPP_* event unhandled %d", event);
log_i("ESP_SPP_* event #%d unhandled", event);
break;
}
if(custom_spp_callback)(*custom_spp_callback)(event, param);
@ -416,10 +423,11 @@ void BluetoothSerial::onData(BluetoothSerialDataCb cb){
custom_data_callback = cb;
}
static void esp_bt_gap_cb(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *param)
{
switch(event){
case ESP_BT_GAP_DISC_RES_EVT: {
case ESP_BT_GAP_DISC_RES_EVT: { // Enum 0 - Device discovery result event
log_i("ESP_BT_GAP_DISC_RES_EVT properties=%d", param->disc_res.num_prop);
#if (ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO)
char bda_str[18];
@ -430,7 +438,44 @@ static void esp_bt_gap_cb(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *pa
char peer_bdname[ESP_BT_GAP_MAX_BDNAME_LEN + 1];
for (int i = 0; i < param->disc_res.num_prop; i++) {
switch(param->disc_res.prop[i].type) {
case ESP_BT_GAP_DEV_PROP_EIR:
case ESP_BT_GAP_DEV_PROP_BDNAME: // Enum 1 - Bluetooth device name, value type is int8_t []
peer_bdname_len = param->disc_res.prop[i].len;
memcpy(peer_bdname, param->disc_res.prop[i].val, peer_bdname_len);
peer_bdname_len--; // len includes 0 terminator
log_v("ESP_BT_GAP_DISC_RES_EVT : BDNAME : %s : %d", peer_bdname, peer_bdname_len);
if (strlen(_remote_name) == peer_bdname_len
&& strncmp(peer_bdname, _remote_name, peer_bdname_len) == 0) {
log_i("ESP_BT_GAP_DISC_RES_EVT : SPP_START_DISCOVERY_BDNAME : %s", peer_bdname);
_isRemoteAddressSet = true;
memcpy(_peer_bd_addr, param->disc_res.bda, ESP_BD_ADDR_LEN);
esp_bt_gap_cancel_discovery();
esp_spp_start_discovery(_peer_bd_addr);
}
break;
case ESP_BT_GAP_DEV_PROP_COD: // Enum 2 - Class of Device, value type is uint32_t
if (param->disc_res.prop[i].len <= sizeof(int)) {
uint32_t cod = 0;
memcpy(&cod, param->disc_res.prop[i].val, param->disc_res.prop[i].len);
advertisedDevice.setCOD(cod);
log_d("ESP_BT_GAP_DEV_PROP_COD 0x%x", cod);
} else {
log_d("ESP_BT_GAP_DEV_PROP_COD invalid COD: Value size larger than integer");
}
break;
case ESP_BT_GAP_DEV_PROP_RSSI: // Enum 3 - Received Signal strength Indication, value type is int8_t, ranging from -128 to 127
if (param->disc_res.prop[i].len <= sizeof(int)) {
uint8_t rssi = 0;
memcpy(&rssi, param->disc_res.prop[i].val, param->disc_res.prop[i].len);
log_d("ESP_BT_GAP_DEV_PROP_RSSI %d", rssi);
advertisedDevice.setRSSI(rssi);
} else {
log_d("ESP_BT_GAP_DEV_PROP_RSSI invalid RSSI: Value size larger than integer");
}
break;
case ESP_BT_GAP_DEV_PROP_EIR: // Enum 4 - Extended Inquiry Response, value type is uint8_t []
if (get_name_from_eir((uint8_t*)param->disc_res.prop[i].val, peer_bdname, &peer_bdname_len)) {
log_i("ESP_BT_GAP_DISC_RES_EVT : EIR : %s : %d", peer_bdname, peer_bdname_len);
if (strlen(_remote_name) == peer_bdname_len
@ -444,43 +489,6 @@ static void esp_bt_gap_cb(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *pa
}
break;
case ESP_BT_GAP_DEV_PROP_BDNAME:
peer_bdname_len = param->disc_res.prop[i].len;
memcpy(peer_bdname, param->disc_res.prop[i].val, peer_bdname_len);
peer_bdname_len--; // len includes 0 terminator
log_v("ESP_BT_GAP_DISC_RES_EVT : BDNAME : %s : %d", peer_bdname, peer_bdname_len);
if (strlen(_remote_name) == peer_bdname_len
&& strncmp(peer_bdname, _remote_name, peer_bdname_len) == 0) {
log_i("ESP_BT_GAP_DISC_RES_EVT : SPP_START_DISCOVERY_BDNAME : %s", peer_bdname);
_isRemoteAddressSet = true;
memcpy(_peer_bd_addr, param->disc_res.bda, ESP_BD_ADDR_LEN);
esp_bt_gap_cancel_discovery();
esp_spp_start_discovery(_peer_bd_addr);
}
break;
case ESP_BT_GAP_DEV_PROP_COD:
if (param->disc_res.prop[i].len <= sizeof(int)) {
uint32_t cod = 0;
memcpy(&cod, param->disc_res.prop[i].val, param->disc_res.prop[i].len);
advertisedDevice.setCOD(cod);
log_d("ESP_BT_GAP_DEV_PROP_COD 0x%x", cod);
} else {
log_d("ESP_BT_GAP_DEV_PROP_COD invalid COD: Value size larger than integer");
}
break;
case ESP_BT_GAP_DEV_PROP_RSSI:
if (param->disc_res.prop[i].len <= sizeof(int)) {
uint8_t rssi = 0;
memcpy(&rssi, param->disc_res.prop[i].val, param->disc_res.prop[i].len);
log_d("ESP_BT_GAP_DEV_PROP_RSSI %d", rssi);
advertisedDevice.setRSSI(rssi);
} else {
log_d("ESP_BT_GAP_DEV_PROP_RSSI invalid RSSI: Value size larger than integer");
}
break;
default:
log_i("ESP_BT_GAP_DISC_RES_EVT unknown property [%d]:type:%d", i, param->disc_res.prop[i].type);
break;
@ -498,7 +506,7 @@ static void esp_bt_gap_cb(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *pa
}
break;
case ESP_BT_GAP_DISC_STATE_CHANGED_EVT:
case ESP_BT_GAP_DISC_STATE_CHANGED_EVT: // Enum 1 - Discovery state changed event
if (param->disc_st_chg.state == ESP_BT_GAP_DISCOVERY_STOPPED) {
log_i("ESP_BT_GAP_DISC_STATE_CHANGED_EVT stopped");
xEventGroupClearBits(_bt_event_group, BT_DISCOVERY_RUNNING);
@ -510,15 +518,15 @@ static void esp_bt_gap_cb(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *pa
}
break;
case ESP_BT_GAP_RMT_SRVCS_EVT:
case ESP_BT_GAP_RMT_SRVCS_EVT: // Enum 2 - Get remote services event
log_i( "ESP_BT_GAP_RMT_SRVCS_EVT: status = %d, num_uuids = %d", param->rmt_srvcs.stat, param->rmt_srvcs.num_uuids);
break;
case ESP_BT_GAP_RMT_SRVC_REC_EVT:
case ESP_BT_GAP_RMT_SRVC_REC_EVT: // Enum 3 - Get remote service record event
log_i("ESP_BT_GAP_RMT_SRVC_REC_EVT: status = %d", param->rmt_srvc_rec.stat);
break;
case ESP_BT_GAP_AUTH_CMPL_EVT:
case ESP_BT_GAP_AUTH_CMPL_EVT: // Enum 4 - Authentication complete event
if (param->auth_cmpl.stat == ESP_BT_STATUS_SUCCESS) {
log_v("authentication success: %s", param->auth_cmpl.device_name);
if (auth_complete_callback) {
@ -531,58 +539,89 @@ static void esp_bt_gap_cb(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *pa
}
}
break;
case ESP_BT_GAP_PIN_REQ_EVT:
// default pairing pins
log_i("ESP_BT_GAP_PIN_REQ_EVT min_16_digit:%d", param->pin_req.min_16_digit);
if (param->pin_req.min_16_digit) {
log_i("Input pin code: 0000 0000 0000 0000");
esp_bt_pin_code_t pin_code;
memset(pin_code, '0', ESP_BT_PIN_CODE_LEN);
esp_bt_gap_pin_reply(param->pin_req.bda, true, 16, pin_code);
case ESP_BT_GAP_PIN_REQ_EVT: // Enum 5 - Legacy Pairing Pin code request
log_i("ESP_BT_GAP_PIN_REQ_EVT (min_16_digit=%d)", param->pin_req.min_16_digit);
if (param->pin_req.min_16_digit && _pin_code_len < 16) {
esp_bt_gap_pin_reply(param->pin_req.bda, false, 0, NULL);
} else {
log_i("Input pin code: 1234");
esp_bt_pin_code_t pin_code;
memcpy(pin_code, "1234", 4);
esp_bt_gap_pin_reply(param->pin_req.bda, true, 4, pin_code);
//log_i("Input pin code: \"%s\"=0x%x", _pin_code);
log_i("Input pin code: \"%.*s\"=0x%x", _pin_code_len, _pin_code, *(int*)_pin_code);
esp_bt_gap_pin_reply(param->pin_req.bda, true, _pin_code_len, _pin_code);
}
break;
case ESP_BT_GAP_CFM_REQ_EVT:
#ifdef CONFIG_BT_SSP_ENABLED
case ESP_BT_GAP_CFM_REQ_EVT: // Enum 6 - Security Simple Pairing User Confirmation request.
log_i("ESP_BT_GAP_CFM_REQ_EVT Please compare the numeric value: %d", param->cfm_req.num_val);
if (confirm_request_callback) {
memcpy(current_bd_addr, param->cfm_req.bda, sizeof(esp_bd_addr_t));
confirm_request_callback(param->cfm_req.num_val);
}
else {
esp_bt_gap_ssp_confirm_reply(param->cfm_req.bda, true);
log_w("ESP_BT_GAP_CFM_REQ_EVT: confirm_request_callback does not exist - refusing pairing");
esp_bt_gap_ssp_confirm_reply(param->cfm_req.bda, false);
}
break;
#endif
case ESP_BT_GAP_KEY_NOTIF_EVT:
case ESP_BT_GAP_KEY_NOTIF_EVT: // Enum 7 - Security Simple Pairing Passkey Notification
log_i("ESP_BT_GAP_KEY_NOTIF_EVT passkey:%d", param->key_notif.passkey);
break;
case ESP_BT_GAP_KEY_REQ_EVT:
#ifdef CONFIG_BT_SSP_ENABLED
case ESP_BT_GAP_KEY_REQ_EVT: // Enum 8 - Security Simple Pairing Passkey request
log_i("ESP_BT_GAP_KEY_REQ_EVT Please enter passkey!");
if (key_request_callback) {
memcpy(current_bd_addr, param->cfm_req.bda, sizeof(esp_bd_addr_t));
key_request_callback();
} else {
log_w("ESP_BT_GAP_KEY_REQ_EVT: key_request_callback does not exist - refuseing pairing");
esp_bt_gap_ssp_confirm_reply(param->cfm_req.bda, false);
}
break;
#endif
case ESP_BT_GAP_CONFIG_EIR_DATA_EVT:
case ESP_BT_GAP_READ_RSSI_DELTA_EVT: // Enum 9 - Read rssi event
log_i("ESP_BT_GAP_READ_RSSI_DELTA_EVT Read rssi event");
break;
case ESP_BT_GAP_CONFIG_EIR_DATA_EVT: // Enum 10 - Config EIR data event
log_i("ESP_BT_GAP_CONFIG_EIR_DATA_EVT: stat:%d num:%d", param->config_eir_data.stat, param->config_eir_data.eir_type_num);
break;
case ESP_BT_GAP_READ_REMOTE_NAME_EVT:
case ESP_BT_GAP_SET_AFH_CHANNELS_EVT: // Enum 11 - Set AFH channels event
log_i("ESP_BT_GAP_SET_AFH_CHANNELS_EVT Set AFH channels event");
break;
case ESP_BT_GAP_READ_REMOTE_NAME_EVT: // Enum 12 - Read Remote Name event
if (param->read_rmt_name.stat == ESP_BT_STATUS_SUCCESS ) {
log_i("ESP_BT_GAP_READ_REMOTE_NAME_EVT: %s", param->read_rmt_name.rmt_name);
memcpy(_rmt_name, param->read_rmt_name.rmt_name, ESP_BT_GAP_MAX_BDNAME_LEN + 1);
_rmt_name_valid = true;
} else {
log_i("ESP_BT_GAP_READ_REMOTE_NAME_EVT: no success stat:%d", param->read_rmt_name.stat);
}
break;
case ESP_BT_GAP_MODE_CHG_EVT:
case ESP_BT_GAP_MODE_CHG_EVT: // Enum 13
log_i("ESP_BT_GAP_MODE_CHG_EVT: mode: %d", param->mode_chg.mode);
break;
case ESP_BT_GAP_REMOVE_BOND_DEV_COMPLETE_EVT: // Enum - 14 remove bond device complete event
log_i("ESP_BT_GAP_REMOVE_BOND_DEV_COMPLETE_EVT remove bond device complete event");
break;
case ESP_BT_GAP_QOS_CMPL_EVT: // Enum 15 - QOS complete event
log_i("ESP_BT_GAP_QOS_CMPL_EVT QOS complete event");
break;
case ESP_BT_GAP_ACL_CONN_CMPL_STAT_EVT: // Enum 16 - ACL connection complete status event
log_i("ESP_BT_GAP_ACL_CONN_CMPL_STAT_EVT ACL connection complete status event");
break;
case ESP_BT_GAP_ACL_DISCONN_CMPL_STAT_EVT: // Enum 17 - ACL disconnection complete status event
log_i("ESP_BT_GAP_ACL_DISCONN_CMPL_STAT_EVT ACL disconnection complete status event: reason %d, handle %d", param->acl_disconn_cmpl_stat.reason, param->acl_disconn_cmpl_stat.handle);
break;
default:
log_i("ESP-BT_GAP_* unknown message: %d", event);
break;
@ -678,24 +717,26 @@ static bool _init_bt(const char *deviceName)
return false;
}
// if (esp_bt_sleep_disable() != ESP_OK){
// log_e("esp_bt_sleep_disable failed");
// }
log_i("device name set");
esp_bt_dev_set_device_name(deviceName);
if (_isPinSet) {
log_i("pin set");
btSetPin();
}
#ifdef CONFIG_BT_SSP_ENABLED
if (_enableSSP) {
log_i("Simple Secure Pairing");
esp_bt_sp_param_t param_type = ESP_BT_SP_IOCAP_MODE;
esp_bt_io_cap_t iocap = ESP_BT_IO_CAP_IO;
esp_bt_io_cap_t iocap;
if(_IO_CAP_INPUT && _IO_CAP_OUTPUT){
iocap = ESP_BT_IO_CAP_IO; // Display with prompt
}else if(!_IO_CAP_INPUT && _IO_CAP_OUTPUT){
iocap = ESP_BT_IO_CAP_OUT; // DisplayOnly
}else if(_IO_CAP_INPUT && !_IO_CAP_OUTPUT){
iocap = ESP_BT_IO_CAP_IN; // Input only
}else if(!_IO_CAP_INPUT && !_IO_CAP_OUTPUT){
iocap = ESP_BT_IO_CAP_NONE; // No input/output
}
esp_bt_gap_set_security_param(param_type, &iocap, sizeof(uint8_t));
}
#endif
// the default BTA_DM_COD_LOUDSPEAKER does not work with the macOS BT stack
esp_bt_cod_t cod;
@ -871,11 +912,22 @@ void BluetoothSerial::end()
_stop_bt();
}
#ifdef CONFIG_BT_SSP_ENABLED
void BluetoothSerial::onConfirmRequest(ConfirmRequestCb cb)
{
confirm_request_callback = cb;
}
void BluetoothSerial::onKeyRequest(KeyRequestCb cb)
{
key_request_callback = cb;
}
void BluetoothSerial::respondPasskey(uint32_t passkey){
esp_bt_gap_ssp_passkey_reply(current_bd_addr, true, passkey);
}
#endif
void BluetoothSerial::onAuthComplete(AuthCompleteCb cb)
{
auth_complete_callback = cb;
@ -883,7 +935,7 @@ void BluetoothSerial::onAuthComplete(AuthCompleteCb cb)
void BluetoothSerial::confirmReply(boolean confirm)
{
esp_bt_gap_ssp_confirm_reply(current_bd_addr, confirm);
esp_bt_gap_ssp_confirm_reply(current_bd_addr, confirm);
}
@ -893,33 +945,55 @@ esp_err_t BluetoothSerial::register_callback(esp_spp_cb_t * callback)
return ESP_OK;
}
//Simple Secure Pairing
#ifdef CONFIG_BT_SSP_ENABLED
// Enable Simple Secure Pairing (using generated PIN)
// This must be called before calling begin, otherwise has no effect!
void BluetoothSerial::enableSSP() {
if(isReady(false, READY_TIMEOUT)){
log_i("Attempted to enable SSP for already initialized driver. Restart to take effect with end() followed by begin()");
return;
}
_enableSSP = true;
_IO_CAP_INPUT = true;
_IO_CAP_OUTPUT = true;
}
/*
* Set default parameters for Legacy Pairing
* Use fixed pin code
*/
bool BluetoothSerial::setPin(const char *pin) {
log_i("pin: %s", pin);
bool isEmpty = !(pin && *pin);
if (isEmpty && !_isPinSet) {
return true; // nothing to do
} else if (!isEmpty){
_pin_len = strlen(pin);
memcpy(_pin_code, pin, _pin_len);
} else {
_pin_len = 0; // resetting pin to none (default)
}
_pin_code[_pin_len] = 0;
_isPinSet = true;
if (isReady(false, READY_TIMEOUT)) {
btSetPin();
}
return true;
// Enable Simple Secure Pairing (using generated PIN)
// This must be called before calling begin, otherwise has no effect!
// Behavior description:
// When both Input and Output are false only the other device authenticates pairing without any pin.
// When Output is true and Input is false only the other device authenticates pairing without any pin.
// When both Input and Output are true both devices display randomly generated code and if they match authenticate pairing on both devices
// - This must be implemented by registering callback via onConfirmRequest() and in this callback request user input and call confirmReply(true); if the authenticated
// otherwise call `confirmReply(false)` to reject the pairing.
// When Input is true and Output is false User will be required to input the passkey to the ESP32 device to authenticate.
// - This must be implemented by registering callback via onKeyRequest() and in this callback the entered passkey will be responded via respondPasskey(passkey);
void BluetoothSerial::enableSSP(bool inputCpability, bool outputCapability) {
log_i("Enabling SSP: input capability=%d; output capability=%d", inputCpability, outputCapability);
_enableSSP = true;
_IO_CAP_INPUT = inputCpability;
_IO_CAP_OUTPUT = outputCapability;
}
// Disable Simple Secure Pairing (using generated PIN)
// This must be called before calling begin, otherwise has no effect!
void BluetoothSerial::disableSSP() {
_enableSSP = false;
}
#else
bool BluetoothSerial::setPin(const char *pin, uint8_t pin_code_len){
if(pin_code_len == 0 || pin_code_len > 16){
log_e("PIN code must be 1-16 Bytes long! Called with length %d", pin_code_len);
return false;
}
_pin_code_len = pin_code_len;
memcpy(_pin_code, pin, pin_code_len);
return (esp_bt_gap_set_pin(ESP_BT_PIN_TYPE_FIXED, _pin_code_len, _pin_code) == ESP_OK);
}
#endif
bool BluetoothSerial::connect(String remoteName)
{
bool retval = false;
@ -931,9 +1005,9 @@ bool BluetoothSerial::connect(String remoteName)
}
disconnect();
_doConnect = true;
_isRemoteAddressSet = false;
_sec_mask = ESP_SPP_SEC_ENCRYPT|ESP_SPP_SEC_AUTHENTICATE;
_role = ESP_SPP_ROLE_MASTER;
_isRemoteAddressSet = true;
_sec_mask = ESP_SPP_SEC_ENCRYPT|ESP_SPP_SEC_AUTHENTICATE;
_role = ESP_SPP_ROLE_MASTER;
strncpy(_remote_name, remoteName.c_str(), ESP_BT_GAP_MAX_BDNAME_LEN);
_remote_name[ESP_BT_GAP_MAX_BDNAME_LEN] = 0;
log_i("master : remoteName");
@ -974,8 +1048,8 @@ bool BluetoothSerial::connect(uint8_t remoteAddress[], int channel, esp_spp_sec_
_doConnect = true;
_remote_name[0] = 0;
_isRemoteAddressSet = true;
_sec_mask = sec_mask;
_role = role;
_sec_mask = sec_mask;
_role = role;
memcpy(_peer_bd_addr, remoteAddress, ESP_BD_ADDR_LEN);
log_i("master : remoteAddress");
xEventGroupClearBits(_spp_event_group, SPP_CLOSED);
@ -987,7 +1061,7 @@ bool BluetoothSerial::connect(uint8_t remoteAddress[], int channel, esp_spp_sec_
channel);
#endif
if(esp_spp_connect(sec_mask, role, channel, _peer_bd_addr) != ESP_OK ) {
log_e("spp connect failed");
log_e("spp connect failed");
retval = false;
} else {
retval = waitForConnect(READY_TIMEOUT);
@ -1088,14 +1162,16 @@ bool BluetoothSerial::isReady(bool checkMaster, int timeout) {
/**
* @brief RemoteName or address are not allowed to be set during discovery
* (otherwhise it might connect automatically and stop discovery)
* (otherwise it might connect automatically and stop discovery)
* @param[in] timeoutMs can range from MIN_INQ_TIME to MAX_INQ_TIME
* @return in case of Error immediately Empty ScanResults.
*/
BTScanResults* BluetoothSerial::discover(int timeoutMs) {
scanResults.clear();
if (timeoutMs < MIN_INQ_TIME || timeoutMs > MAX_INQ_TIME || strlen(_remote_name) || _isRemoteAddressSet)
if (timeoutMs < MIN_INQ_TIME || timeoutMs > MAX_INQ_TIME){
log_e("Timeout out of bounds: MIN=%d; MAX=%d; requested=%d", MIN_INQ_TIME, MAX_INQ_TIME, timeoutMs);
return nullptr;
}
int timeout = timeoutMs / INQ_TIME;
log_i("discover::disconnect");
disconnect();
@ -1112,11 +1188,11 @@ BTScanResults* BluetoothSerial::discover(int timeoutMs) {
/**
* @brief RemoteName or address are not allowed to be set during discovery
* (otherwhise it might connect automatically and stop discovery)
* (otherwise it might connect automatically and stop discovery)
* @param[in] cb called when a [b]new[/b] device has been discovered
* @param[in] timeoutMs can be 0 or range from MIN_INQ_TIME to MAX_INQ_TIME
*
* @return Wheter start was successfull or problems with params
* @return Whether start was successful or problems with params
*/
bool BluetoothSerial::discoverAsync(BTAdvertisedDeviceCb cb, int timeoutMs) {
scanResults.clear();
@ -1139,7 +1215,7 @@ void BluetoothSerial::discoverAsyncStop() {
advertisedDeviceCb = nullptr;
}
/** @brief Clears scanresult entries */
/** @brief Clears scanResult entries */
void BluetoothSerial::discoverClear() {
scanResults.clear();
}
@ -1211,4 +1287,108 @@ BTAddress BluetoothSerial::getBtAddressObject() {
String BluetoothSerial::getBtAddressString() {
return getBtAddressObject().toString(true);
}
#endif
// Send a request to the remote device defined by the remoteAddress to send back its name.
// The name will be read by background task and stored. It can be later read with radRemoteName()
void BluetoothSerial::requestRemoteName(uint8_t remoteAddress[]){
if(isReady(false, READY_TIMEOUT)){
esp_bt_gap_read_remote_name(remoteAddress);
}
}
// If remote name is valid (was already received) this function will copy the name to the aprameter rmt_name
// The buffer must have size at least ESP_BT_GAP_MAX_BDNAME_LEN + 1
// If the name is valid the function will return true
// If the name is not valid (was not read yet) returns false
bool BluetoothSerial::readRemoteName(char rmt_name[ESP_BT_GAP_MAX_BDNAME_LEN + 1]){
if(_rmt_name_valid){
memcpy(rmt_name, _rmt_name, ESP_BT_GAP_MAX_BDNAME_LEN + 1);
return true;
}
return false;
}
// Set validity of remote name before reading name from different device
void BluetoothSerial::invalidateRemoteName(){
_rmt_name_valid = false;
}
int BluetoothSerial::getNumberOfBondedDevices(){
return esp_bt_gap_get_bond_device_num();
}
// Accepts the maximum number of devices that can fit in given array dev_list.
// Create you list this way: esp_bd_addr_t dev_list[dev_num];
// Returns number of retrieved devices (on error returns 0)
int BluetoothSerial::getBondedDevices(uint dev_num, esp_bd_addr_t *dev_list){
// typedef uint8_t esp_bd_addr_t[ESP_BD_ADDR_LEN]
if(dev_list == NULL){
log_e("Device list is NULL");
return 0;
}
if(dev_num == 0){
log_e("Device number must be larger than 0!");
return 0;
}
int _dev_num = dev_num;
esp_bt_gap_get_bond_device_list(&_dev_num, dev_list);
return _dev_num;
}
bool BluetoothSerial::deleteBondedDevice(uint8_t *remoteAddress){
esp_err_t ret = esp_bt_gap_remove_bond_device(remoteAddress);
if(ret == ESP_OK){
return true;
}else{
return false;
}
}
void BluetoothSerial::deleteAllBondedDevices(){
if(!isReady(false, READY_TIMEOUT)){
log_w("Attempted to drop cache for uninitialized driver. First call begin()");
return;
}
int expected_dev_num = esp_bt_gap_get_bond_device_num();
if(expected_dev_num == 0){
log_i("No devices in cache.");
return;
} else {
log_d("Found %d bonded devices", expected_dev_num);
}
esp_err_t ret;
// typedef uint8_t esp_bd_addr_t[ESP_BD_ADDR_LEN] // ESP_BD_ADDR_LEN = 6
esp_bd_addr_t *dev_list = NULL;
log_d("Allocate buffer: sizeof(esp_bd_addr_t)=%d * expected_dev_num=%d", sizeof(esp_bd_addr_t), expected_dev_num);
dev_list = (esp_bd_addr_t*) malloc(sizeof(esp_bd_addr_t) * expected_dev_num);
if(dev_list == NULL){
log_e("Could not allocated BT device buffer!");
return;
}
//uint8_t dev_list [20][6];
int dev_num;
ret = esp_bt_gap_get_bond_device_list(&dev_num, dev_list);
log_d("esp_bt_gap_get_bond_device_list ret = %d", ret);
if(ret == ESP_OK){
if(dev_num != expected_dev_num){
log_w("Inconsistent number of bonded devices. Expected %d; returned %d",expected_dev_num, dev_num);
}
for(int i=0; i<dev_num; ++i){
ret = esp_bt_gap_remove_bond_device(dev_list[i]);
log_d("esp_bt_gap_remove_bond_device ret = %d", ret);
if(ret == ESP_OK){
log_d("Removed bonded device #%d", i);
} else {
log_w("Failed to removed bonded device #%d", i);
}
//btc_storage_remove_bonded_device(dev_list[i]);
}
log_d("device num after delete = %d", esp_bt_gap_get_bond_device_num());
}else{
log_w("Function esp_bt_gap_get_bond_device_list() returned code %d", ret);
}
}
#endif // defined(CONFIG_BT_ENABLED) && defined(CONFIG_BLUEDROID_ENABLED)

View file

@ -26,9 +26,12 @@
#include <functional>
#include <map>
#include "BTScan.h"
#include "BTAdvertisedDevice.h"
typedef std::function<void(const uint8_t *buffer, size_t size)> BluetoothSerialDataCb;
typedef std::function<void(uint32_t num_val)> ConfirmRequestCb;
typedef std::function<void()> KeyRequestCb;
typedef std::function<void(boolean success)> AuthCompleteCb;
typedef std::function<void(BTAdvertisedDevice* pAdvertisedDevice)> BTAdvertisedDeviceCb;
@ -55,16 +58,25 @@ class BluetoothSerial: public Stream
void onData(BluetoothSerialDataCb cb);
esp_err_t register_callback(esp_spp_cb_t * callback);
#ifdef CONFIG_BT_SSP_ENABLED
void onConfirmRequest(ConfirmRequestCb cb);
void onKeyRequest(KeyRequestCb cb);
void respondPasskey(uint32_t passkey);
#endif
void onAuthComplete(AuthCompleteCb cb);
void confirmReply(boolean confirm);
#ifdef CONFIG_BT_SSP_ENABLED
void enableSSP();
bool setPin(const char *pin);
void enableSSP(bool inputCapability, bool outputCapability);
void disableSSP();
#else
bool setPin(const char *pin, uint8_t pin_code_len);
#endif
bool connect(String remoteName);
bool connect(uint8_t remoteAddress[], int channel=0, esp_spp_sec_t sec_mask=(ESP_SPP_SEC_ENCRYPT|ESP_SPP_SEC_AUTHENTICATE), esp_spp_role_t role=ESP_SPP_ROLE_MASTER);
bool connect(const BTAddress &remoteAddress, int channel=0, esp_spp_sec_t sec_mask=(ESP_SPP_SEC_ENCRYPT|ESP_SPP_SEC_AUTHENTICATE), esp_spp_role_t role=ESP_SPP_ROLE_MASTER) {
return connect(*remoteAddress.getNative(), channel, sec_mask); };
return connect(*remoteAddress.getNative(), channel, sec_mask); };
bool connect();
bool connected(int timeout=0);
bool isClosed();
@ -88,6 +100,14 @@ class BluetoothSerial: public Stream
void getBtAddress(uint8_t *mac);
BTAddress getBtAddressObject();
String getBtAddressString();
//void dropCache(); // To be replaced
void requestRemoteName(uint8_t *remoteAddress);
bool readRemoteName(char rmt_name[ESP_BT_GAP_MAX_BDNAME_LEN + 1]);
void invalidateRemoteName();
int getNumberOfBondedDevices();
int getBondedDevices(uint dev_num, esp_bd_addr_t *dev_list);
bool deleteBondedDevice(uint8_t *remoteAddress);
void deleteAllBondedDevices();
private:
String local_name;
int timeoutTicks=0;